Appearance
添加反向依赖
本节目标:理解“添加反向依赖”的核心思路,并能把它用于实际开发或面试表达。 单向是指属性记录了Effect; 反向是指让effect也记录了它被哪些属性收集过; 这样做的好处是为了可以清理;
使用反向依赖的例子:
effect(() => {
flag ? this.name : this.age
})
flag为true时,name收集了effect;
flag为false时,age收集了effect;此时,我们要把name收集的effect清空,这时要通过effect将所有的记录都删掉,再把age的加进去;export let activeEffect = undefined;
// 拥有一个run方法,就是执行effect
class ReactiveEffect {
==public== ==deps== ===== ==[];==
public parent = null;
// 创建的effect默认是激活的
public active = true
// public修饰符会在实例上添加这个参数作为属性
// 相当于this.fn=fn
constructor(public fn) { }
run() {
// 如果是非激活的,只需要执行函数,不需要进行依赖收集
if (!this.active) {
this.fn();
}
try {
// 第一个实例的parent是undefined
this.parent = activeEffect;
activeEffect = this;
return this.fn();
} finally {
// 代码执行完后,
// 一、重置activeEffect,指向parent
activeEffect = this.parent;
// 二、重置parent指针,undefined?
this.parent = null;
}
}
}
export const effect = (fn) => {
// 这里的fn可以根据数据变化,重新执行;
// effect可以嵌套着写
// 创建响应式的effect
const _effect = new ReactiveEffect(fn);
// 默认先执行一次
_effect.run();
}
const targetMap = new WeakMap();
export function track(target, type, key) {
// 如果不是在effect中获取属性,是不跟踪的;
if (!activeEffect) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
// 将源对象与一个map关联起来;
// 这个map的键是属性名,值是一个set,set中放置的是effect实例
// 当map中的属性变化时,我们把对应的set中的effect实例拿出来,调用其run方法;
// 从而又执行了一次effect的fn函数;
targetMap.set(target, (depsMap = new Map()));
}
// 再到这个depsMap中查找当前属性对应的Set;
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
// 看set中是否存在当前的effect
let shouldTrack = !dep.has(activeEffect);
if (shouldTrack) {
dep.add(activeEffect);
// effect实例deps属性是一个数组,里面存在着set,set中存放着属性依赖的effect;
// 稍后清理的时候会用到;
// 如果effect用到了两个属性,deps数组就会有两个成员set;
==activeEffect====.====deps====.====push====(====dep====);==
}
}