VUE中v-for更新检测

Posted 25氪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VUE中v-for更新检测相关的知识,希望对你有一定的参考价值。

口诀:
  • 数组变更方法,就会导致 v-for 更新,页面更新
  • 数组非变更方法:返回新数组,就不会导致 v-for 更新,更新值检测不到可采用覆盖或者 this.$set()
数组变更方法如下:
1. arr.push()从后面添加元素
	arr.push(5)
2. arr.pop()从后面删除元素,只能是一个
	arr.pop()
3. arr.shift()从前面删除元素,只能删除一个
	arr.shift()
4. arr.unshift()从前面添加元素,返回值时添加完后数组长度
	arr.unshift(8)
5. arr.splice(i,n)删除从 i (索引值)开始之后删除的 N(删除的个数)个数
let arr = [1,2,3,4,5]
console.log(arr.splice(2,2))     //[3,4]
console.log(arr)    // [1,2,5]
6. arr.sort()将数组进行排序,返回值排好的数组
let arr = [2,10,6,1,4,22,3]
console.log(arr.sort())   // [1, 10, 2, 22, 3, 4, 6]
let arr1 = arr.sort((a, b) =>a - b)  
console.log(arr1)   // [1, 2, 3, 4, 6, 10, 22]
let arr2 = arr.sort((a, b) =>b-a)  
console.log(arr2)  // [22, 10, 6, 4, 3, 2, 1]
7. arr.reverse() 将数组反转
let arr = [1,2,3,4,5]
console.log(arr.reverse())    // [5,4,3,2,1]
console.log(arr)    // [5,4,3,2,1]
数组非变更方法如下:
1. arr.concat()连接两个数组
let arr = [1,2,3,4,5]
console.log(arr.concat([1,2]))  // [1,2,3,4,5,1,2]
console.log(arr)   // [1,2,3,4,5]
2. arr.slice(start,end)切去索引 start 到 end 索引值,不包含 start 索引值
let arr = [1,2,3,4,5]
console.log(arr.slice(1,3))   // [2,3]

覆盖方法

<li v-for="(val, index) in arr" :key="index">
     {{ val }}
   </li>
   <button @click="sliceBtn">截取前3个</button>
   
    sliceBtn(){
   // 2. 数组slice方法不会造成v-for更新
   // slice不会改变原始数组
   // this.arr.slice(0, 3)

   // 解决v-for更新 - 覆盖原始数组
   let newArr = this.arr.slice(0, 3)
   this.arr = newArr
 },

this.$set()方法

<li v-for="(val, index) in arr" :key="index">
     {{ val }}
   </li>
   <button @click="sliceBtn">更新下标0的值</button>
   
   sliceBtn(){
   // 更新某个值时,v-for是检测不到的
   // this.arr[0] = 1000
   
   // 解决- this.$set()
   // 参数1:更新目标结构
   // 参数2:更新位置
   // 参数3:更新值
   let newArr = this.arr.slice(0, 3)
   this.arr = newArr
 },

Vue:如何在 v-for 中执行响应式对象更改检测?

【中文标题】Vue:如何在 v-for 中执行响应式对象更改检测?【英文标题】:Vue: How to perform reactive object change detection in v-for? 【发布时间】:2019-05-02 13:59:06 【问题描述】:

我阅读了this 文档,但无法使用建议的解决方案。

我有一个v-for 循环对象。这些对象随时间动态变化,我需要这种变化在 v-for 循环中反应性地显示。

<b-row lg="12" v-for="data in objects" :key="data.id">
    <div v-if="data.loading">
      loading...
      data.loading
    </div>
    <div v-else>
      loaded, done
      data.loading
    </div>
</b-row>

在我的方法中,我有一个 for 循环,它为每个对象下载数据并像这样更改对象值:

for(var i = 0; i<response.ids.length; i++)
    var newId = response.ids[i].id
    this.objects.newId = "loading":true, "id": newId

    downloadSomething(newId).then(res => 
        this.objects.newId = res[0]         //<-- this change needs to be shown reactively.
    )

根据 Vue 文档,对象更改不是反应性的:

var vm = new Vue(
  data: 
    a: 1
  
)
// `vm.a` is now reactive

vm.b = 2
// `vm.b` is NOT reactive

Vue 提出了一些这样的解决方法:

Vue.set(vm.userProfile, 'age', 27)

更新

但对我来说,这只是在对象中创建一个具有相同 ID 的新参数,并创建重复键警告和其他问题。

我也在 Vue.set 之前尝试了Vue.delete,但它实际上并没有删除。

有没有办法不替换键/值对,而是向 ID 为 newID 的根的第一个子节点添加更多/更改参数

谢谢!

【问题讨论】:

【参考方案1】:

解决方案:this.objects.newId = res[0] 替换为this.$set(this.objects, 'newId', res[0]),它应该可以工作。

解释: this.$set 只是 Vue.set 的别名,可在任何 Vue 实例中使用。请记住,示例中的vm(代表视图模型)与this 相同,相当于Vue 组件实例。同样由于 ES5 JS 的限制,您必须通过 Vue.set/this.$set 显式设置对象的属性才能手动触发重新渲染。这个问题会在 Vue 3.0 发布后解决。

希望这会有所帮助,如果您需要任何说明,请随时提问。

【讨论】:

我没有明确提到我的“对象”是一个对象而不是数组。 Vue.set/vm.$set 在 'objects' 中创建一个具有相同 ID 的新参数,并且不会替换现有参数。这会产生重复键警告和其他问题。我也在 Vue.set 之前尝试了 Vue.delete,但 Vue.delete 不起作用。 想通了。我在没有 Vue.set 的情况下为子对象设置初始键值,然后期望它随着 Vue.set 改变。事实证明,还需要使用 Vue.set 设置初始值(加载值)。我还有助于提高可读性。【参考方案2】:

当您想为现有对象(而不是直接定义数据)定义 新属性 时,您需要Vue.set(),并且此函数会将新属性分配给内置观察程序,它会也变得被动。

这一切都在文档中进行了解释:https://vuejs.org/v2/guide/reactivity.html

在你的情况下,这似乎是你让它工作所需要的。在我的示例中:https://jsfiddle.net/efrat19/eywraw8t/484131/ 一个异步函数获取数据并定义一个新属性以包含响应。

new Vue(
  el: "#app",
  data: 
    objects:
    property1:12
    
  ,
  methods: 
    fetchDataAndAddProperty(response)
        fetch('https://free.currencyconverterapi.com/api/v6/countries').then(res =>
      res.json()).then(data => Vue.set(this.objects,'property2',data))
    
  
)

和模板:

<div id="app">
<div lg="12" v-for="(val,key,index) in objects" :key="index">
    key:val
</div>
<button @click="fetchDataAndAddProperty()">fetch</button>
</div>

正如您在小提琴中看到的那样,新属性变为反应性并被显示。

【讨论】:

【参考方案3】:

试试看:

downloadSomething(newId).then(res => 
    this.$set(this.objects, 'newId', res[0])
)

【讨论】:

以上是关于VUE中v-for更新检测的主要内容,如果未能解决你的问题,请参考以下文章

vue中v-for 怎么获取长度

Vue:如何在 v-for 中执行响应式对象更改检测?

在 bootstrap-vue datepicker 中检测 v-for 中每个元素的目标

检测bootstrap-vue日期选择器中v-for中每个元素的目标

Vue v-for 不更新模型变量

为啥 Vue 更新所有组件,而不仅仅是 v-for 中更改的组件?