这一小节实现effect的stop功能。
一、单元测试编写
首先,编写单元测试部分代码。主要有2点:
- 调用effect的stop,修改响应式对象的值时,effect传入的ffn不再执行
- 当effect传入第二个可选参数onStop函数时,调用effect的stop,将执行onStop函数
it("stop",() => {
let dummy;
const obj = reactive({prop: 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);
});
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);
})
二、功能实现
实现要点:
- 通过在ReactiveEffect类中添加stop方法,当调用stop方法时,将effect的依赖集合deps都删除
import {extend} from '../shared'
class ReactiveEffect{
private _fn:any;
deps = [];
active = true;
onStop?:() => void;
constructor(fn, public scheduler?){
this._fn = fn;
}
run(){
activeEffect = this;
return this._fn();
}
stop(){
if(this.active){
cleanupEffect(this);
if(this.onStop){
this.onStop();
}
this.active = false;
}
cleanupEffect(this);
}
}
function cleanupEffect(effect){
effect.deps.forEach((dep: any) => {
dep.delete(effect);
})
}
const targetMap = new Map();
export function track(target,key){
// target -> key -> dep
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)
}
if(!activeEffect) return;
dep.add(activeEffect);
activeEffect.deps.push(dep);
}
export function trigger(target,key){
let depsMap = targetMap.get(target);
let dep = depsMap.get(key);
for(const effect of dep){
if(effect.scheduler){
effect.scheduler();
} else {
effect.run();
}
}
}
let activeEffect;
export function effect(fn, options: any= {}){
const _effect = new ReactiveEffect(fn,options.scheduler);
// options
Object.assign(_effect,options);
// extend
extend(_effect,options);
_effect.onStop = options.onStop;
_effect.run();
const runner: any = _effect.run.bind(_effect);
runner.effect = _effect;
return runner
}
export function stop(runner){
runner.effect.stop()
}
三、执行单元测试
功能实现过程中,我们可以运行yarn test effect --watch,添加--watch参数,当我们修改代码时,将自动进行测试脚本运行。
yarn test effect --watch
整个过程完成后,我们可以运行yarn test,运行所有的测试,以防止对其他功能的影响。
yarn test