vue3.0源码中computed实现分析

110 阅读2分钟

我们先看源码实现, 为了方便,我去掉了一些开发环境和SSR情况下的逻辑分支判断的代码。

代码位置:\core\packages\reactivity\src\computed.ts

class ComputedRefImpl {
  public dep: any;
  public effect: ReactiveEffect;

  private _dirty: boolean;
  private _value

  constructor(getter) {
    this._dirty = true;
    this.dep = createDep();
    this.effect = new ReactiveEffect(getter, () => {
      // scheduler
      // 只要触发了这个函数说明响应式对象的值发生改变了
      // 那么就解锁,后续在调用 get 的时候就会重新执行,所以会得到最新的值
      if (this._dirty) return;
      // 解锁
      this._dirty = true;
       // 触发依赖
      triggerRefValue(this);
    });
  }

  get value() {
    // 收集依赖
    trackRefValue(this);
    // 锁上,只可以调用一次
    // 当数据改变的时候才会解锁
    // 这里就是缓存实现的核心
    // 解锁动作是在上面的 scheduler 里面做的
    if (this._dirty) {
      this._dirty = false;
      // 这里执行 run 的话,其实就是执行用户传入的 fn
      this._value = this.effect.run();
    }

    return this._value;
  }
}
function computed(getterOrOptions) {
  let getter;
  let setter;
  if (isFunction(getterOrOptions)) {
    getter = getterOrOptions;
    setter =  () => {
      console.warn('Write operation failed: computed value is readonly');
    };
  }
  else {
    getter = getterOrOptions.get;
    setter = getterOrOptions.set;
  }
  return new ComputedRefImpl(getter, setter, isFunction(getterOrOptions) || !getterOrOptions.set);
}

一般使用场景,举个栗子: 1、接受一个getter函数,并为getter返回的值返回一个只读的响应式ref对象。 2、它还可以使用具有get和set函数的对象来创建可写的ref对象。

 // 通过以下方式创建出来的是一个只读的 ref
 const count = ref(1)
 const plusOne = computed(() => count.value + 1)

 console.log(plusOne.value) // 2
 plusOne.value++ // 如果修改plusOne的值会报提示:Write operation failed: computed value is readonly
// 想要创建一个可读可写的ref对象,通过以下的方式:
// 可以使用具有get和set函数的对象来创建可写的ref对象。
const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

Q&A

有人可能会疑惑为什么要增加_dirty这个变量? 这是因为要做缓存的缘故,保证getter函数里面的响应对象的值发生改变了才会再次触发fn,如果值没有改变,则读取缓存里面的值,提高性能。

总结:

1、解析源码中computed方法的实现方式,实现逻辑比较简单,理解了_dirty基本上就理解了computed的缓存核心 2、例举了两种场景,如果传入的getter是个函数的话,computed返回的就是一个只读的响应式ref对象,如果传入的getter中有set和get函数,则可以创建一个可写的ref对象 3、解释了_dirty变量的作用