目标
- 为 effect 提供一个 stop 方法
- 当使用 stop 传入 runner 调用时会停止调用 effect
- 当手动调用 runner 时,又会调用 effect ```typescript import { effect, stop } from ‘../effect’;
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);
// stopped effect should still be manually callable runner(); expect(dummy).toBe(3); });
<a name="bk7Ag"></a># 实现```typescriptclass EffectReactive {private _fn;// 增加一个属性存储依赖deps = [];// 是否激活状态active = true;constructor(fn, public scheduler?) {this._fn = fn;}run() {activeEffect = this;return this._fn();}stop() {// 如果已经清空过(失活了),即使外部多次调用也不会清空if(this.active) {// 清空依赖cleanupEffect(effect);this.active = false;}}}function cleanupEffect(effect) {effect.deps.forEach((dep: any) => {dep.delete(effect);});}let activeEffect;export function track(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 Set();depsMap.set(key, dep);}// 重新运行所有单测时,会发现 effect 的 happy path 不通过// activeEffect 有可能是 undefined,会导致 activeEffect.deps 报错if (!activeEffect) return;dep.add(activeEffect);// 为当 track 时反向存储一下对应的依赖,以便 stop 时作清空activeEffect.deps.push(dep);}export function effect(fn, options: any = {}) {const _effect = new EffectReactive(fn, options.scheduler);_effect.run();const runner: any = _effect.run.bind(_effect);// 在 runner 挂载当前的 effectrunner.effect = _effect;return runner;}export function stop(runner) {// 指向 EffectReactive 的 stop 方法runner.effect.stop();}
onStop 目标
- 通过 effect 第二个 options 参数的属性传入 onStop 函数
当调用 stop 之后会被调用 onStop 回调函数
it('onStop', () => {const obj = reactive({ foo: 1 });const onStop = jest.fn();let dummy;const runner = effect(() => {dummy = obj.foo;},{ onStop });stop(runner);expect(onStop).toBeCalledTimes(1);});
实现 onStop
```typescript class EffectReactive { private _fn; deps = []; active = true;
// 声明一个 onStop onStop?: () => void;
constructor(fn, public scheduler?) { this._fn = fn; } run() { activeEffect = this; return this._fn(); } stop() { if(this.active) { cleanupEffect(effect); // 清除后,如果存在 onStop 就调用 if(this.onStop) {
this.onStop();
} this.active = false; } } }
export function effect(fn, options: any = {}) { const _effect = new EffectReactive(fn, options.scheduler); _effect.onStop = options.onStop;
_effect.run();
const runner: any = _effect.run.bind(_effect); // 在 runner 挂载当前的 effect runner.effect = _effect;
return runner; }
<a name="aLQZf"></a># 重构优化```typescriptimport { extend } from '../shared';export function effect(fn, options: any = {}) {const _effect = new EffectReactive(fn, options.scheduler);// 因为后面可能存在更多的 options, 可通过 Object.assign 进行挂载// _effect.onStop = options.onStop;// Object.assign(_effect, options);// // 可以为 Object.assign 重构得更有语义化,在 shared 公共工具函数定义 extend 方法extend(_effect, options);_effect.run();const runner: any = _effect.run.bind(_effect);runner.effect = _effect;return runner;}
export const extend = Object.assign;
