第43题 说一下vue2.x中如何监测数组变化
使用了函数劫持的方式,重写了数组的方法,
Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。
// 源码实现
/*
* not type checking this file because flow doesn't play well with
* dynamically accessing methods on Array prototype
*/
/**
* Define a property.
*/
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
/**
* Intercept mutating methods and emit events
*/
;[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
.forEach(function (method) {
// cache original 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)
// notify change
ob.dep.notify()
return result
})
})
补充(现代做法):以上是 Vue2 的做法——因为
Object.defineProperty无法侦测数组「按下标赋值」和「修改 length」,Vue2 只能重写 7 个变异方法(push/pop/shift/unshift/splice/sort/reverse)来手动触发更新,所以this.arr[0]=x、this.arr.length=0不会响应,要用Vue.set/splice。Vue3 改用Proxy整体代理对象/数组,原生就能拦截下标赋值、新增属性、delete、length变化,不再需要重写数组方法,也不再有Vue.set的限制。
