- vue中数组的一些方法是如何进行试图更新的?
- vue中我们对数组进行push,splice,shift的一些操作时候也会触发render-watcher。这是因为vue中对这些数组的方法进行了一些扩展,使其能够进行数据的响应式,源码如下:
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
// arrayMethods 就是 继承自 Array.prototype 的数组
// 只需要让响应式数组 继承子 arrayMethods
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.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__ // 是否响应式的标志,保存了依赖收集对象deps
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
})
})
- 但在vue中对数组的下标赋值处理时,是不会触发视图的更新,于是vue提供了一个静态方法;set、del;数组是通过splice进行操作的,这里列举一个set源码:
export function set (target: Array<any> | Object, key: any, val: any): any {
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val) // 利用扩展的splice方法进行响应式
return val
}
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
return val
}
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val); // 让子项响应式
ob.dep.notify(); // 手动派发更新
return val
}
- 简化原理;
- 一个数组的原型链对应如:arr.proto->Array.prototype-> .proto->Object.prototype;
- 所以我们可以添加一个__proto__;arr.proto->(Object.create(Array.prototype))->Array.prototype-> .proto->Object.prototype;
let ARRAY_METHODS = [
'push',
'pop',
'shift',
'unshift',
'splice',
'reverse',
'sort'
];
let array_methods = Object.create(Array.prototype);
ARRAY_METHODS.forEach(method=>{
array_methods[method] = function(){
console.log('拦截了数组的方法',this);
let res = Array.prototype[method].apply(this,arguments);
return res;
}
});
let arr = [];
arr.__proto__ = array_methods;