这一小节实现effect的scheduler功能。
编写单元测试
首先写一下单元测试。主要有4点:
- 通过effect的第二个参数给定的一个scheduler的fn
- effect第一次执行的时候,还会执行fn(effect的第一个参数)
- 当响应式对象set、update时不会执行fn(effect的第一个参数),而是执行scheduler
- 当执行runner的时候,会再次的执行fn
it("scheduler", () => {
let dummy;
let run:any;
const scheduler = jest.fn(()=> {
run = runner;
});
const obj = reactive({foo:1});
const runner = effect(
() => {
dummy = obj.foo;
},
{scheduler}
);
expect(scheduler).not.toHaveBeenCalled();
expect(dummy).toBe(1);
// should be called on first trigger
obj.foo++;
expect(scheduler).toHaveBeenCalledTimes(1);
// should not run yet
expect(dummy).toBe(1);
// manually run
run();
// should have run
expect(dummy).toBe(2);
})
功能实现
class ReactiveEffect{
private _fn:any;
constructor(fn,public scheduler?){
this._fn = fn;
}
run(){
activeEffect = this;
return this._fn();
}
}
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)
}
dep.add(activeEffect);
}
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 scheduler = options.scheduler;
const _effect = new ReactiveEffect(fn,scheduler);
_effect.run();
return _effect.run.bind(_effect)
}
具体修改点可以看下图
执行单元测试
功能实现后我们可以执行单元测试:
yarn test effect
可以看到测试通过