目标
- 为 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>
# 实现
```typescript
class 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 挂载当前的 effect
runner.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>
# 重构优化
```typescript
import { 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;