Object.defineProperty不支持监听数组变化。所以需要重写数组上面的方法。
vue2.0对数组的方法进行了劫持,重写了能改变数组的方法实现数据的更新
const arrayProto = Array.prototype//原生Array的原型
export const arrayMethods = Object.create(arrayProto);
[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
].forEach(function (method) {
const original = arrayProto[method]//缓存元素数组原型
//这里重写了数组的几个原型方法
def(arrayMethods, method, function mutator () {
//这里备份一份参数应该是从性能方面的考虑
let i = arguments.length
const args = new Array(i)
while (i--) {
args[i] = arguments[i]
}
const result = original.apply(this, args)//原始方法求值
const ob = this.__ob__//这里this.__ob__指向的是数据的Observer
let inserted
switch (method) {
case 'push':
inserted = args
break
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})
//定义属性
function def (obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
上面的代码主要是继承了Array本身的原型方法,然后又做了劫持修改,可以发出通知。
Vue在observer数据阶段会判断如果是数组的话,则修改数组的原型,这样的话,后面对数组的任何操作都可以在劫持的过程中控制。
// 以上代码 相当于
var bar = [1,2];
bar.__proto__ = arrayMethod;
// 执行
bar.push(3); 就会触发arrayMethods中的代码:ob.dep.notify()就会被触发,就会通知watcher触发template的更新。