vue3都来了。才开始写vue2...
vu2的响应式核心在与对object的属性的getter setter拦截,使用Object.defineProperty
Object.defineProperty(obj, key, {
get() {
console.log("getter")
return obj[key];
},
set(newValue) {
console.log("setter")
obj[key] = newValue;
}
});
这样就可以在get的时候收集依赖,set的时候通知依赖
先写一个依赖收集的类
class Dep{
constructe(){
this.deps = new Set()
}
depend(update){
this.deps.add(update)
}
notfiy(){
this.deps.forEach(update=>update())
}
}
这样就可以的把依赖存起来,但是有个问题,update是对哪个属性值依赖呢?
这就得先执行下依赖函数,看下他get了哪个key就是哪个key的update
let dep = new Dep()
Object.defineProperty(obj, key, {
get() {
console.log("getter")
dep.depend(()=>{
console.log("依赖")
})
return obj[key];
},
set(newValue) {
console.log("setter")
obj[key] = newValue;
dep.notfiy()
}
});
但是不能把依赖写在getter里,所以要把依赖单个存起来
let dep = new Dep()
let update =()=>{console.log("依赖")}
Object.defineProperty(obj, key, {
get() {
console.log("getter")
dep.depend(update)
return obj[key];
},
...
但这样还是要吧update传进去,所以还需要一个能从depend里直接去收集,而且依赖有很多个,但是依赖都是一个一个调用的,所以只需要一个全局变量保存当前调用的依赖就可以了
let update = ()=>{console.log("依赖")}
//update是可变的
let active = update
...
depend(){
this.deps.add(active)
}
...
let dep = new Dep()
Object.defineProperty(obj, key, {
get() {
console.log("getter")
dep.depend()
return obj[key];
},
...
那么,什么时候active变呢?这是就依赖需要一个主动调用的过程
function autorun(update) {
const wrappedUpdate = () => {
active = wrappedUpdate;
update();
active = null;
};
wrappedUpdate();
}
那么简单的原理就差不多了。
//拦截getter setter
function obsever(obj) {
if (typeof obj != "object" || obj === null) {
return;
}
Object.keys(obj).forEach((key) => {
let value = obj[key];
let dep = new Dep();
Object.defineProperty(obj, key, {
get() {
dep.depend();
return value;
},
set(newValue) {
const changed = newValue !== value;
value = newValue;
changed && dep.notify();
}
});
//递归拦截
obsever(value);
});
}
//依赖订阅通知
class Dep {
constructor() {
this.deps = new Set();
}
depend() {
activeUpdate && this.deps.add(activeUpdate);
}
notify() {
this.deps.forEach((fn) => fn());
}
}
//autorun 建立依赖
function autorun(update) {
const wrappedUpdate = () => {
activeUpdate = wrappedUpdate;
update();
activeUpdate = null;
};
wrappedUpdate();
}
let activeUpdate;
let state = {
a: {
b: 2
},
c: 1
};
//响应式
obsever(state);
//建立依赖
autorun(() => {
console.log(state.a.b);
});
state.c++;