八股文說,vue2使用Object.definedPropoty,而Vue3使用的是Proxy。這很難理解,于是我深入調(diào)查一番原理層面,如下純手寫,非AI。
如果我是Vue開發(fā)者,想要實(shí)現(xiàn)“雙向數(shù)據(jù)綁定”的需求,也就是對(duì)象obj中的數(shù)據(jù),被讀取或者被更新之后,我都要想辦法收到通知,并且能夠做一些其他的操作,比如觸發(fā)頁面更新等操作。
const const obj = {
a: 1,
b: 2,
c: {
d: 3,
e: 4,
},
};
obj.a // 我并不知道“讀取”操作了
obj.a = 1 // 我并不知道“更新”操作了
上面的操作,我沒法收到通知,做相關(guān)的處理,那怎么辦呢?
我要是能重新修改obj對(duì)象的set、get方法就好了,這樣“讀取”會(huì)默認(rèn)走get方法,“更新”會(huì)默認(rèn)走set方法。
Object.definedPropoty就可以做到,實(shí)現(xiàn)代碼如下:
// vue2
function observer(dataSource: { [key: string]: any }) {
for (let key in dataSource) { // [注釋1]
const value = dataSource[key];
if(typeof value === 'object' && value !== null) {
observer(value); // [注釋2]
}
const listener = Object.defineProperty(dataSource, key, {
set: (sth) => {
if(key === sth) return;
console.log('修改更新', key);
key = sth;
},
get: () => {
console.log('讀取', key);
return value;
},
});
listener;
}
return dataSource;
}
const listener = observer(obj);
listener.c.d;
listener.c.d = 1111;
listener.c.d;
console.log('lister<<<<<<<<', listener);
注釋1:Object.defineProperty只能寫對(duì)應(yīng)的key,因此每次需要遍歷整個(gè)對(duì)象所有的key,依次包裹。這也是Vue2的弊端——需要提前整體遍歷對(duì)象,效率比較差。而且只能操作已有的數(shù)據(jù)。
注釋2:如果對(duì)象包含了對(duì)象,需要做遞歸處理,而這一步,需要操作之前就要執(zhí)行,效率差。
以上就是Vue2實(shí)現(xiàn)雙向數(shù)據(jù)綁定的原理,總結(jié)來說,Vue2有以下弊端:
- 需要提前整體遍歷對(duì)象每一個(gè)數(shù)據(jù),效率低;
- 數(shù)組的方法無法觸發(fā)set\get,因此Vue2對(duì)數(shù)組的方法做了重構(gòu);
- 只能對(duì)已有的數(shù)據(jù)做操作,如果新增數(shù)據(jù),比如obj['aaa'],則無法獲得監(jiān)聽,因?yàn)闆]有被Object.definedPropoty包裹。
那么Vue3做了什么呢?
// vue3
function isObject(obj: any) {
return typeof obj === 'object' && obj !== null;
}
function observer(dataSource: { [key: string]: any }) {
const proxy = new Proxy(dataSource, {
get(target, prop) {
if (isObject(target[prop as keyof typeof target])) {
return observer(target[prop as keyof typeof target]);
}
console.log('讀取操作');
return target;
},
set(target, prop, val) {
if (target.prop === val) return;
if (isObject(target[prop as keyof typeof target])) {
return observer(target[prop as keyof typeof target]);
}
target[prop as keyof typeof target] = val;
console.log('更新操作');
return true;
},
});
return proxy;
}
Proxy可以自動(dòng)監(jiān)聽整個(gè)對(duì)象,而且支持?jǐn)?shù)組,無需提前遍歷。
the end!