目标
处理一些关于 stop 的 edge case
在 obj.prop ++ 时,会同时触发 getter 和 setter,因为 getter 会导致重新依赖收集。导致 stop 失败
it('stop', () => {let dummy;const obj = reactive({ foo: 1 });const runner = effect(() => {dummy = obj.prop;});obj.prop = 2;expect(dummy).toBe(2);stop(runner);obj.prop = 3;expect(dummy).toBe(2);// ******* bugobj.prop ++; // obj.prop = obj.prop + 1; get -> setexpect(dummy).toBe(2);// stopped effect should still be manually callablerunner();expect(dummy).toBe(4);});
优化
可以对依赖收集 track 时做一些文章
let activeEffect;let shouldTrack;class EffectReactive {private _fn;deps = [];active = true;onStop?: () => void;constructor(fn, public scheduler?) {this._fn = fn;}run() {// 1. 会收集依赖// 使用 shouldTrack 来区分if (!this.active) {return this._fn();}shouldTrack = true;activeEffect = this;const result = this._fn();// resetshouldTrack = false;return result;}stop() {if (this.active) {cleanupEffect(this);if (this.onStop) {this.onStop();}this.active = false;}}}function cleanupEffect(effect) {effect.deps.forEach((dep: any) => {dep.delete(effect);});// dep 都清空了,对应的 effect.deps 就没有意义,可清空作优化effect.deps.length = 0;}export function track(target, key) {// 重构// if (!activeEffect) return;// if (!shouldTrack) return;if(!isTracking()) return;let depsMap = targetMap.get(target);if (!depsMap) {depsMap = new Map();targetMap.set(target, depsMap);}let dep = depsMap.get(key);if (!dep) {dep = new Set();depsMap.set(key, dep);}// 已经在 dep 中if (dep.has(activeEffect)) return;dep.add(activeEffect);activeEffect.deps.push(dep);}function isTracking() {return shouldTrack && activeEffect !== undefined;}
