vue 数组响应式实现
Vue 数组响应式实现原理
Vue 通过重写数组的原型方法来实现对数组操作的响应式监听。具体实现基于以下机制:
拦截数组变异方法
Vue 创建了一个继承自原生 Array 原型的对象,并重写了以下会改变原数组的方法:
- push
- pop
- shift
- unshift
- splice
- sort
- reverse
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method) {
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
ob.dep.notify()
return result
})
})
依赖收集与通知
每个被观察的数组都有一个关联的 Observer 实例,其中包含一个 Dep 依赖收集器。当数组被修改时,会通过 dep.notify() 通知所有依赖的 Watcher 进行更新。
数组元素观测
对于数组中的对象元素,Vue 会递归地进行响应式处理:
Observer.prototype.observeArray = function observeArray (items) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
处理数组索引修改
直接通过索引修改数组元素或修改数组长度不会触发响应式更新:
// 不会触发响应式更新
vm.items[0] = newValue
vm.items.length = 0
对于这种情况,应该使用 Vue.set 或数组的 splice 方法:
Vue.set(vm.items, 0, newValue)
vm.items.splice(0, 1, newValue)
实现细节
- 在初始化阶段,Vue 会检查数据是否为数组,如果是则修改其 proto 指向重写后的数组方法对象
- 对于新增的数组元素(如通过 push 添加的元素),会进行额外的响应式处理
- 数组的依赖收集与其他响应式数据使用相同的 Dep/Watcher 机制
这种实现方式既保持了 JavaScript 数组的原生性能,又实现了响应式更新,是 Vue 响应式系统的重要设计之一。







