Vue 数组元素修改与视图更新

Vue 中直接修改数组元素不会触发视图更新的原因及解决方案

问题

在 Vue 的 data 中有一个数组,直接通过索引修改数组元素时,视图是否会更新?

解答

不会触发视图更新

直接通过索引修改数组元素(如 this.array[0] = newValue)不会触发视图更新。

Vue 使用 Object.defineProperty 将 data 对象的属性转为 getter/setter 来追踪依赖变化。每个组件实例都有对应的 watcher,当依赖项的 setter 被调用时,会通知 watcher 重新计算并更新视图。但 Object.defineProperty 无法检测到数组索引的直接赋值操作。

触发视图更新的方法

1. 使用 Vue.set

通过索引设置数组元素:

Vue.set(this.array, 0, newValue)
// 或在组件中
this.$set(this.array, 0, newValue)

设置对象属性:

Vue.set(this.obj, 'newKey', newValue)

2. 使用 Vue.delete

删除数组元素:

Vue.delete(this.array, 0)

删除对象属性:

Vue.delete(this.obj, 'key')

3. 修改数组元素的属性

直接修改数组中对象的属性可以触发更新:

this.array[0].isShow = true

this.array.forEach(function(item) {
    item.isShow = true
})

4. 替换整个数组

this.array = this.array.filter(item => item.id > 10)

5. 使用数组变异方法

Vue 包裹了以下数组方法,调用它们会触发视图更新:

this.array.push(newItem)
this.array.pop()
this.array.shift()
this.array.unshift(newItem)
this.array.splice(0, 1, newItem)
this.array.sort()
this.array.reverse()

关键点

  • 直接通过索引修改数组元素不会触发视图更新,因为 Object.defineProperty 无法检测数组索引变化
  • 使用 Vue.setthis.$set 可以响应式地修改数组元素或对象属性
  • 修改数组中对象的属性可以触发更新,因为对象属性已被转为 getter/setter
  • Vue 包裹了 7 个数组变异方法(push、pop、shift、unshift、splice、sort、reverse),调用它们会触发视图更新
  • 替换整个数组也能触发更新