Vue:Array变化侦测
1. 数组追踪变化
与Object不同,数组无法通过getter和setter方式来追踪变化,因此,我们需要自定义一个拦截器来追踪变化。
2. 拦截器的准备
拦截器其实就是一个与Array.prototype一样的Object,里面所包含的属性一样,但是改变数组的方法是我们修改过的。
首先,我们先对Array.prototype来个定制。
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
let methods = ['push','pop','shift','unshift','splice','sort','reverse'];
methods.forEach(function(method){
// 获取原始方法
const original = arrayMethods[method];
Object.defineProperty(arrayMethods,method,{
value: function mutator(...args){
return original.apply(this,args);
},
enumerable: true,
writable: true,
configurable: true
})
})
- 上面代码中,我们对Array.prototype进行了拷贝,创建出新的对象arrayMethods,其中的mutator就是我们追踪变化的关键,未来将对其进行扩展。
3. 使用拦截器覆盖Array原型
要将一个数据转化成响应式的数据,需要通过Observer,在Observer里使用拦截器覆盖那些即将被转换成响应式的Array类型数据:
const hasProto = '__proto__' in {
}; // 检测是否可用proto
const arrayKeys = Object.getOwnPropertyNames(arrayMethods);
class Observer{
constructor(value){
this.value = value;
if(Array.isArray(value){
// 根据是否支持ES6来对原型对象进行覆盖
const arguments = hasProto? 'protoArguments' : 'copyArguments';
arguments(value,arrayMethods,arrayKeys);
}else{
this.walk(value);
})
}
......
}
function protoArguments(target,src,keys){
target.__proto__ = src;
}
// 递归复制属性
function copyArguments(target,src,keys){
for(let i=0;i<keys.length;i++