概述
Vue3的一個重大升級就是使用 proxy 來做數據劫持,我們來體驗一下用 proxy 是怎么做數據劫持的,供以后工作時參考,相信對其它人也有用。
Vue2.x的缺點
Vue2.x是使用Object.defineProperty
來做數據劫持的,但是它有以下三個缺點:
1.不能劫持數組的變化,需要做特殊處理(通過劫持數組的push、splice等方法實現的)
2.必須深度遍歷對象的每個屬性
3.無法監聽屬性的新增和刪除操作(通過Vue.set 和 Vue.delete實現的)
使用 proxy
1.劫持數組的變化
const test = [];
const testProxy = new Proxy(test, {
get(target, key) {
if (key !== 'length') {
console.log('get==================', target, key);
}
return target[key];
},
set(target, key, value) {
if (key !== 'length') {
console.log('set==================', target, key);
}
return Reflect.set(target, key, value);
}
});
testProxy.push(1);
testProxy[0] = 2;
testProxy[1] = 'abc';
這里需要注意的是,這里使用push的時候,length屬性會發生變化,所以上面過濾掉了length屬性。(當然我這里做的比較簡單,而 Vue3 源碼里面是通過判斷 push、indexOf、shift 等方法來分情況做的。)
2.必須深度遍歷對象的每個屬性
const toProxy = new WeakMap();
const toRaw = new WeakMap();
const isObject = val => typeof val === 'object' && val !== null;
const proxyConfig = {
get(target, key) {
const res = Reflect.get(target, key);
if (isObject(res)) {
return reactive(res);
}
console.log('get==================', target, key);
return res;
},
set(target, key, value) {
console.log('set==================', target, key);
return Reflect.set(target, key, value);
}
}
function reactive(target) {
const res = toProxy.get(target);
if (res) {
return res;
}
if (toRaw.get(target)) {
return target;
}
const observed = new Proxy(target, proxyConfig);
toProxy.set(target, observed);
toRaw.set(observed, target);
return observed;
}
const test = {
a: 2,
b: {
c: 'abc',
}
};
const testProxy = reactive(test);
console.log(testProxy.b.c);
testProxy.b.d = 3;
這里需要注意的是,當屬性是深度嵌套的時候,只會觸發 getter,並不會觸發setter,所以需要對深度嵌套的屬性進一步使用 proxy!
但是使用 proxy 得到的新屬性又不能掛載到對象上面去,所以需要使用一個 weakmap 儲存起來,同時也能加快查找速度。
3.監聽屬性的新增和刪除操作
const test = {a:2};
const testProxy = new Proxy(test, {
deleteProperty(target, key) {
console.log('delete==================', target, key);
delete target[key];
},
});
delete testProxy.a;
總結
1.proxy做數據劫持還是有一些痛點的,比如對於數組的push、unshift等方法需要做兼容,對於對象的深層屬性仍然需要通過遍歷來重新proxy。
2.proxy的功能不止如此,它還能攔截更多的屬性,比如 has、defineProperty、apply 等。