在vue3中,采用Proxy进行代理,从而实现数据劫持。
依赖收集器
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect);
}
}
notify() {
this.subscribers.forEach(effect => {
effect();
})
}
}
let activeEffect = null;
function watchEffect(effect) {
activeEffect = effect;
effect();
activeEffect = null;
}
const targetMap = new WeakMap();
function getDep(target, key) {
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let dep = depsMap.get(key);
if (!dep) {
dep = new Dep();
depsMap.set(key, dep);
}
return dep;
}
vue2
function reactive(data) {
Object.keys(data).forEach(key => {
const dep = getDep(data, key);
let value = data[key];
Object.defineProperty(data, key, {
get() {
//收集依赖
dep.depend();
return value;
},
set(newValue) {
if (value !== newValue) {
value = newValue;
//触发依赖
dep.notify();
}
}
})
})
return data;
}
vue3
function reactive(data) {
return new Proxy(data, {
get(target, key) {
const dep = getDep(target, key);
//收集依赖
dep.depend();
return target[key];
},
set(target, key, newValue) {
const dep = getDep(target, key);
target[key] = newValue;
//触发依赖
dep.notify();
}
})
}
使用Proxy
进行数据劫持和 Object.defineProperty
的区别
1.使用Object.defineProperty
进行数据劫持,是对每一个属性进行了劫持,如果新增元素,需要使用$set
将新增的元素进行劫持,而使用Proxy代理则是直接代理了整个对象。所以我们在修改数据是,vue2修改原来的数据就可以进行响应式,在vue3中需要修改的是我们的代理对象proxy。
2.Proxy
能够观察的类型比Object.defineProperty
更丰富,包括has
,delete
等操作。
3.Proxy是新标准。但是不兼容IE。