vue是如何对数组方法进行变异的
由于 js 的限制,在数组使用push
、pop
、splice
等这些方法时,其set
方法是不能感知的,例如如下代码(vue js 中精简的部分):
function defineReactive(obj, key, val) {
const property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return;
}
const getter = property && property.get;
const setter = property && property.set;
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
console.log("取值操作");
return getter ? getter.call(obj) : val;
},
set(v) {
console.log(`设置值${v}`);
if (setter) {
setter.call(obj, v);
} else {
val = v;
return val;
}
}
});
}
此时有如下数据,然后进行相关操作
defineReactive(data, "array", data.array);
const a = data.array[0]; // 取值操作
data.array[0] = 10; // 取值操作
data.array.push(1); // 取值操作
data.array.splice(1); // 取值操作
data.array = [4, 5, 6]; // 设置值4,5,6
从结果可以看出,除了赋值操作,其余的方式都不会被setter
检测到。那么 vue 是怎么处理让其支持数组的相关检测的?
其主要是对数组的这些方法进行变异。以数组的push
操作为例。
const arrayMethod = Object.create(Array.prototype);
const originPush = arrayMethod.push;
Object.defineProperty(arrayMethod, "push", {
enumerable: true,
configurable: true,
writable: true,
value(...args) {
const result = originPush.apply(this, args);
console.log(`对数组push了${JSON.stringify(args)}`);
return result;
}
});
这里,首先将数组的原型放到arrayMethod
的原型中,然后给arrayMethod
定义push
方法,在给数组push
值之前,会先调用数组原型上的push
方法,保证数组正常执行push
方法,然后,再执行剩下的自定义方法,这样就实现了对数组的变异。
对操作数组的方法都执行这样的操作,就实现了类似 Vue 对数据的响应。
以下代码节选自Vue源码。
const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);
const methodsToPatch = [
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
];
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__;
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;
});
});
如果您觉得本文对您有用,欢迎捐赠或留言~
- 本博客所有文章除特别声明外,均可转载和分享,转载请注明出处!
- 本文地址:https://www.leevii.com/?p=1792