这一小节我们继续完善effect。先写一下测试,看看effect功能: effect其实会返回一个function,可以把这个function称为runner,当调用这个function的时候,它会执行我们传给它的fn,并把fn的返回值返回出去。那这个功能怎么实现呢?
it('', () => {
// 1.effect(fn) -> function (runner) -> fn -> return
})
首先写一个测试 第一步,验证effect会执行传入的函数
let foo = 10;
effect(() => {
foo++;
});
expect(foo).toBe(11);
第二步,期望可以拿到一个runner函数,再次调用runner函数时可以拿到返回值.所以我们可以验证2点,第一点fn是否再次执行了,执行的话foo会变为12,第二点,是否有返回值。这就是对功能的一个测试。
let foo = 10;
const runner = effect(() => {
foo++;
return "foo"
});
expect(foo).toBe(11);
const r = runner()
expect(foo).toBe(12)
expect(r).toBe("foo")
接下来在代码中实现。首先effect会调用run,执行传入的函数,并返回函数执行结果的,但是在run方法中会涉及到this指针问题,所以我们需要用bind来绑定this.以当前的effect实例作为this指向。还剩最后一步,当调用run方法,会把函数的返回值return出去。总体实现如下:
class ReactiveEffect{
private _fn:any;
constructor(fn){
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){
effect.run();
}
}
let activeEffect;
export function effect(fn){
const _effect = new ReactiveEffect(fn);
_effect.run();
return _effect.run.bind(_effect)
}
完成后我们可以通过yarn test effect测试功能是否实现。