7.实现effect返回runner

126 阅读1分钟

这一小节我们继续完善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测试功能是否实现。

代码地址:github.com/zhangchongy…