// 存储当前正在执行的副作用函数
let activeEffect;
// 保存依赖的地方
const bucket = new WeakMap();
// 副作用函数是可以嵌套执行的,所以我们需要一个 stack 来保存嵌套的副作用函数,方便执行完副作用函数之后正确的回退到外层副作用函数
const effectStack = [];
// 追踪依赖
function track(target, key) {
if (!activeEffect) return;
let depsMap = bucket.get(target);
if (!depsMap) {
bucket.set(target, (depsMap = new Map()));
}
let deps = depsMap.get(key);
if (!deps) {
depsMap.set(key, (deps = new Set()));
}
deps.add(activeEffect);
activeEffect.deps.push(deps);
}
// 触发依赖
function trigger(target, key) {
const depsMap = bucket.get(target);
if (!depsMap) return;
const effects = depsMap.get(key);
const effectsToRun = new Set();
effects &&
effects.forEach((effectFn) => {
// 解决无限递归的问题
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn);
}
});
effectsToRun.forEach((fn) => fn());
}
//
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i];
deps.delete(effectFn);
}
effectFn.deps.length = 0;
}
// 副作用函数
function effect(fn) {
const effectFn = () => {
// 在执行 effectFn 函数之前,需要清空遗留的副作用函数
// 比如在第一次执行副作用函数的时候使用到了 a 变量,第二次执行副作用函数的时候没有使用到 a 变量,如果我们不及时清除副作用函数
// 即使我们没有使用 a 变量了,a 变量的改变也会导致副作用函数的重新执行。
cleanup(effectFn);
activeEffect = effectFn;
// 执行辅作用函数之前,将当前副作用函数入栈
effectStack.push(activeEffect);
fn();
// 副作用函数执行完毕后,出栈
effectStack.pop();
// 将当前副作用函数设置为栈顶元素,也就是外层副作用函数
activeEffect = effectStack[effectStack.length - 1];
};
// 用来保存当前副作用函数被哪些依赖收集了
effectFn.deps = [];
effectFn();
}
// 将一个对象转换为响应式
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
// 收集依赖
track(target, key);
return result;
},
set(target, key, newVal, receiver) {
const result = Reflect.set(target, key, newVal, receiver);
// 触发依赖
trigger(target, key);
return result;
},
});
}
const obj = {
count: 1,
};
const data = reactive(obj);
function render() {
document.body.innerText = data.count;
}
effect(render);
setTimeout(() => {
data.count++;
}, 1000);
该文章仅作为个人学习记录,若对你有帮助,还请点赞支持;若你觉得对你无用,请无视,勿喷!