一个是 vue3 的响应式原理,一个是 vue2 的,那两者的实质性区别到底是什么?我们分别来分析一下各自的特点。
Proxy
它在 MDN 上的解读是这样的:
The Proxy object enables you to create a proxy for another object,which can intercept and redefine fundamental operations for that object.
意思就是能为一个对象创建一个 「proxy」,并且这个 「proxy」 能拦截和重新定义原来对象的基本操作。
你可以这么分析,大雷音寺是一个对象,小雷音寺是代理,用来拦截或给唐僧制造 「陷阱」 的。
那什么又是对象的 「基本操作」 呢? 赋值
还是 读取属性
又或者说是 遍历
?其实这些都是在「语法层面」上的操作。这其实是 js
这门语言为了让开发者使用起来更加方便或者是让程序的「可读性」更好,搞出来的一种独特的语法。那实际上,我们在使用语法的时候,会转换为对应的函数。
1
2
3
obj.a; // 【GET】
obj.b = 3; //【SET】
delete obj.a; //【DELETE】
例如在「读取对象属性」的时候,运行的函数是 GET
。这些内部运行的方法就是它的基本操作。
打开 ecma 262 文档看看对象的内部方法(6.1.7.2)。
而我们在执行 Object.defineProperty
这个方法的时候,内部执行的就是 DefineOwnProperty
。
来看 proxy 的方法介绍,它有一堆的内部方法,每一个方法都对应一个「捕获器」也可以叫做陷阱,用这个陷阱去拦截。所有的陷阱都是可选的,如果没有定义对应的陷阱,那就会保留源对象的默认行为。
1
2
3
4
5
6
7
8
9
10
11
const obj = {
b: 40,
};
const handler = {
get: function (target, prop) {
console.log("prop", prop);
return target[prop];
},
};
const p = new Proxy(obj, handler);
console.log("p.b", p.b); // 40
这种情况,让 proxy
代理 obj
,那 proxy
就拦截了 obj
的内部方法 [[GET]] ,进而掉进了陷阱函数里了。
defineProperty
defineProperty
就只是在 「执行对象的一个基本操作 defineOwnProperty
」。无法监听,无法拦截。
举一个例子
调用数组的 push
方法,或设置数组的 length
属性。
用 Object.defineProperty
进行处理,直接就报错了(length 属性无法被重新定义)。所以说,直接用通过数组对象去调原型里的 push
是没办法监听到的,是 vue
在中间插入一个对象,这个对象重写了数组的方法。这样实际上我们的数组对象去调的时候,调的其实是 vue
的那个对象,里面重写原型了。
proxy
可以直接拦截到对象的基本操作,所以在陷阱函数里,就可以拦截到 arr.push(1)
的操作,包括是这个 push
属性、 length
属性、里面的变化的值等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const arr = [1, 2, 3];
const p = new Proxy(arr, {
get(target, prop) {
console.log("get", prop);
return target[prop];
},
set(target, prop, value) {
console.log("set", prop, value);
target[prop] = value;
return true;
},
});
p.push(1);
// get push
// get length
// set 3 1
// set length 4