5 Vue中如何检测数组变化
前言
Vue 不能检测到以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue - 当你修改数组的长度时,例如:
vm.items.length = newLength
Vue 提供了以下操作方法
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set,Vue.set的一个别名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
分析
数组考虑性能原因没有用
defineProperty对数组的每一项进行拦截,而是选择对7种数组(push,shift,pop,splice,unshift,sort,reverse)方法进行重写(AOP切片思想)
所以在 Vue 中修改数组的索引和长度是无法监控到的。需要通过以上 7 种变异方法修改数组才会触发数组对应的 watcher 进行更新
- 用函数劫持的方式,重写了数组方法,具体呢就是更改了数组的原型,更改成自己的,用户调数组的一些方法的时候,走的就是自己的方法,然后通知视图去更新
- 数组里每一项可能是对象,那么我就是会对数组的每一项进行观测(且只有数组里的对象才能进行观测,观测过的也不会进行观测)
原理
Vue将data中的数组,进行了原型链重写。指向了自己定义的数组原型方法,这样当调用数组api时,可以通知依赖更新,如果数组中包含着引用类型。会对数组中的引用类型再次进行监控。

手写简版分析
let oldArray = Object.create(Array.prototype);
['shift', 'unshift', 'push', 'pop', 'reverse','sort'].forEach(method => {
oldArray[method] = function() { // 这里可以触发页面更新逻辑
console.log('method', method)
Array.prototype[method].call(this,...arguments);
}
});
let arr = [1,2,3];
arr.__proto__ = oldArray;
arr.unshift(4);
源码分析
// 拿到数组原型拷贝一份
const arrayProto = Array.prototype
// 然后将arrayMethods继承自数组原型
// 这里是面向切片编程思想(AOP)--不破坏封装的前提下,动态的扩展功能
export 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)
// 这句话是关键
// this代表的就是数据本身 比如数据是{a:[1,2,3]} 那么我们使用a.push(4) this就是a ob就是a.__ob__ 这个属性就是上段代码增加的 代表的是该数据已经被响应式观察过了指向Observer实例
const ob = this.__ob__
// 这里的标志就是代表数组有新增操作
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
// 如果有新增的元素 inserted是一个数组 调用Observer实例的observeArray对数组每一项进行观测
if (inserted) ob.observeArray(inserted)
ob.dep.notify() // 当调用数组方法后,手动通知视图更新
return result
})
})
this.observeArray(value) // 进行深度监控
vue3:改用proxy,可直接监听对象数组的变化
