Vue.js中v-for修改数组元素为何不触发重新渲染?
为什么Vue中v-for修改数组索引元素不触发响应?
这个问题是Vue2响应式系统里的一个常见“坑”,我来给你讲明白背后的原因和解决办法~
为什么直接修改索引没反应?
Vue2的响应式是基于Object.defineProperty实现的,这个API可以监听对象属性的读取和修改,但对数组来说,它没办法拦截两种操作:
- 直接通过索引修改数组元素,比如
this.test[3] = 3 - 直接修改数组的
length属性,比如this.test.length = 0
当你执行this.test[3] = 3时,Vue的响应式系统完全没察觉到数据发生了变化,自然不会触发视图的重新渲染。
为什么push之后就生效了?
为了解决数组响应式的问题,Vue2对数组的7个原生方法做了“包装”,这些方法包括:push、pop、shift、unshift、splice、sort、reverse。
被包装后的方法,在执行原生数组操作的同时,会主动触发Vue的响应式更新逻辑,通知视图重新渲染。所以当你先修改索引再调用push时,push触发了更新,之前修改的元素也就跟着显示出来了。
正确的解决办法
如果你想直接修改数组索引对应的元素并触发响应,有几种靠谱的方式:
1. 使用this.$set(或全局的Vue.set)
这是Vue官方推荐的方法,它会主动把新元素加入响应式系统,同时触发视图更新:
changeVar2: function() { this.$set(this.test, 3, 3); }
2. 使用数组的splice方法
splice是被Vue包装过的方法,执行时会触发响应式更新,你可以用它来替换指定索引的元素:
changeVar2: function() { // 参数含义:从索引3的位置,替换1个元素,新元素是3 this.test.splice(3, 1, 3); }
3. 替换整个数组(适合场景化需求)
你可以通过扩展运算符或者其他方式生成一个新数组,替换原数组,因为Vue可以监听数组的替换操作:
changeVar2: function() { this.test = [...this.test, , , 3]; }
补充:Vue3的情况
如果是用Vue3的话,这个问题就不存在了,因为Vue3用Proxy代替了Object.defineProperty,Proxy可以直接拦截数组的索引修改、长度变化等操作,原生的数组操作都能触发响应式更新。
内容的提问来源于stack exchange,提问作者Ilya Egorov




