Vue3---computed源码解析

124 阅读1分钟

步骤一:

computed函数定义在packages/src/reactivity/computed.ts文件中:

export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
  debugOptions?: DebuggerOptions,
  isSSR = false
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>
  // getterOrOptions 即传入的函数
  const onlyGetter = isFunction(getterOrOptions) // 判断 getterOrOptions 是否为函数
  if (onlyGetter) {
    getter = getterOrOptions // 赋值 传入的函数
    setter = __DEV__
      ? () => {
          console.warn('Write operation failed: computed value is readonly') // 理解为空函数
        }
      : NOOP
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }
  // 创建 ComputedRefImpl 实例
  const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)

  if (__DEV__ && debugOptions && !isSSR) {
    cRef.effect.onTrack = debugOptions.onTrack
    cRef.effect.onTrigger = debugOptions.onTrigger
  }
  // 返回 实例
  return cRef as any
}

computed 函数接收一个 getterOrOptions 参数,即传入的匿名函数

步骤二:

创建一个ComputedRefImpl实例,构造函数会创建一个ReactiveEffect实例。

export class ComputedRefImpl<T> {
  public dep?: Dep = undefined

  private _value!: T
  public readonly effect: ReactiveEffect<T>

  public readonly __v_isRef = true
  public readonly [ReactiveFlags.IS_READONLY]: boolean

  public _dirty = true // 脏变量 关键
  public _cacheable: boolean

  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean,
    isSSR: boolean
  ) {
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        this._dirty = true
        triggerRefValue(this) // dirty 为 false 时 触发依赖
      }
    })
    this.effect.computed = this
    this.effect.active = this._cacheable = !isSSR
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    // the computed ref may get wrapped by other proxies e.g. readonly() #3376
    const self = toRaw(this)
    trackRefValue(self) // 依赖收集
    if (self._dirty || !self._cacheable) {
      self._dirty = false
      self._value = self.effect.run()!
    }
    return self._value
  }

  set value(newValue: T) {
    this._setter(newValue)
  }
}

步骤三:执行effect函数

总结:

  • computed计算属性实际是一个ComputedRefImpl构造函数的实例
  • ComputedRefImpl构造函数中通过dirty变量控制effectrun方法的执行和triggerRefValue的触发
    • 先触发computed的effect,再触发非computed的effect【避免多次.value赋值时造成死循环】
  • 每次访问计算属性的值,必须通过.value,执行get value方法,触发trackRefValue`进行依赖收集