Vue2: 数组使用forEach修改,视图不更新的思考

背景:近期项目中,有小伙伴通过数组的forEach方法修改列表的属性值,视图并未更新。

           之前小伙伴一直使用forEach进行同类操作,屡试不爽。

           这次突然不灵了,小小的脑袋有大大的问号。

           对于长期使用数组的map方法的我,并未遇到视图不更新的问题,我不李姐。

使用forEach和map方法,视图更新情况如何?为了彻底搞懂这个问题,下面用一个demo(一个列表的增删改)来验证视图更新情况。

一:Demo

代码准备

<!--template-->
<div v-for="item in list" :key="item.id">{
   
   {item}}</div>
<div class="active-btn" @click="btnClick">btn</div>

<div>{
   
   {textForHtml}}</div>

<script>
// data
list: []
textForData: '', // 只在data中定义
textForHtml: '', // data中定义 且应用到页面中

created () {
  this.textForMethods = '' // 仅在methods 中使用
  this.initDate()
}

// methods
initDate () {
  // 通过定时器模拟ajax获取数据
  setTimeout(() => {
    const list = [
      {id: 0, title: 'title0'},
      {id: 1, title: 'title1'},
      {id: 2, title: 'title2'},
      {id: 3, title: 'title3'}
    ]
    this.list = list
  }, 200)
}

1、新增列表项

    结论:

  1. 利用数组索引—失效
  2. 利用数组push方法—有效
  3. 利用数组unshift方法—有效
  4. 利用数组splice方法—有效
  5. 利用数组concat方法—有效
  6. 利用扩展运算符...—有效
  7. 利用Vue.set—有效
  8. 利用vm.$set—有效

 代码运行结果截图:

方式 结果
索引
push
unshift
splice
concat
扩展运算符...
Vue.set
vm.$set

 2、删除列表项

  结论:

  1. 利用数组长度—失效
  2. 利用数组splice方法—有效
  3. 利用数组pop方法—有效
  4. 利用数组shift方法—有效

代码运行结果截图:

扫描二维码关注公众号,回复: 14859957 查看本文章
方式 结果
数组长度
splice
pop
shift

3、修改属性值—纯修改

结论:

  • 利用直接索引—有效
  • 利用for循环—有效
  • 利用数组forEach—有效
  • 利用map方法获取索引—有效
方式 结果
直接

for

forEach

map

 4、新增属性—纯新增

  • 4.1 利用索引—失效

方式 结果
直接

Object.

assign

Object.

assign

  • 4.2 利用for循环获取索引—失效

方式 结果
直接

Object.

assign

Object.

assign

 4.3 利用forEach/map获取索引,仅修改列表项—失效

方式 结果
直接

Object.

assign

Object.

assign

修改item

Object.

assign

修改

下标值

...
直接

Object.

assign

Object.

assign

修改item

Object.

assign

修改

下标值

...
  • 4.4 利用map方法生成新数组—有效

方式 结果
直接

Object.

assign

Object.

assign

由上可知,

  • 直接使用索引,间接获取索引(本例:for循环,forEach、map【仅修改列表项】方式),新增属性操作(形式不限),结果一致,均失效
  • map方法生成新数组方式,均生效

既然直接使用索引,间接获取索引(本例:for循环,forEach、map【仅修改列表项】)结果一致,下面用forEach方法为代表探讨哪种方式可以使视图更新?

二:forEach方式探讨

序号 结果
1

新增列表项 + 修改一个非data中的值—失效

2

新增列表项 + 修改值【只在data中定义】—失效

3

新增列表项 + 修改值【data中定义 且应用到页面中】—生效

新增列表项 + 不修改值【data中定义 且渲染到页面中,原值不变】—失效

4

新增列表项 + 修改列表项—生效

新增列表项 + 不修改列表项【原值不变】—失效

5

新增多个列表项—失效

6

新增列表项【Vue.set】—生效

7

新增列表项【vm.$set】—生效

三:Vue2官网深入响应式原理一节 检测变化的注意事项

对于数组

Vue 不能检测以下数组的变动:

  • 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue

        解决方法:1)Vue.set:Vue.set(vm.items, indexOfItem, newValue)

                          2)Array.prototype.splice:vm.items.splice(indexOfItem, 1, newValue)

                          3)vm.$set:vm.$set(vm.items, indexOfItem, newValue)

  • 当你修改数组的长度时,例如:vm.items.length = newLength

        解决方法:Array.prototype.splice:vm.items.splice(newLength)

对于对象

Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。

对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。

        解决方法:1)Vue.set:Vue.set(vm.someObject, 'b', 2)

                          2)vm.$set:this.$set(this.someObject,'b',2)

你可能需要为已有对象赋值多个新 property,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。

                        // 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
                        this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

四:总结

最后,对于数组的增删改,结合Vue文档,和我们的代码实例,总结如下:

 总之:

  • 新增和删除数组项时,避免 1)直接利用索引 2)数组长度 的方式
  • 修改数组项时,形式任意
  • 数组项纯新增属性即有可能非响应的,所以,操作属性值建议的写法:
  1. 数组map方法遍历生成一个新数组,操作属性值形式任意
  2. 其他方法修改属性时【如:forEach】:

                1)Vue.set 

                2)vm.$set

                3)列表纯新增属性,且修改值【data中定义 且应用到页面中】

                4)列表同时新增+修改属性 ,这四种方法真实有效。

 forEach纯新增属性,不太行,避免这种写法,但:可以在ajax获取列表时,直接添加所有可能用到的属性

The end.

猜你喜欢

转载自blog.csdn.net/weixin_43932309/article/details/127646259