Vue3源码阅读(七)computed

computed

  • 实现源码在packsages/reactivity/computed.ts
  • 使用
    • 传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象
          const count = ref(1)
          const plusOne = computed(() => count.value + 1)
          console.log(plusOne.value) // 2
          plusOne.value++ // 错误!
      
    • 或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。
          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
      
  • computed也是通过effect实现依赖的收集和触发。不同的是 ComputedRefImpl 加入了_dirty 和 _value。_dirty 用来确认当前计算是否结束,是否需要更新计算结果。_value 用来缓存计算结果。
  • 计算属性,可能会依赖其他 reactive 的值,同时会延迟和缓存计算值
export function computed<T>(
  getter: ComputedGetter<T>,
  debugOptions?: DebuggerOptions
): ComputedRef<T>
export function computed<T>(
  options: WritableComputedOptions<T>,
  debugOptions?: DebuggerOptions
): WritableComputedRef<T>
export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
  debugOptions?: DebuggerOptions
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>
  // 如果传入是 function 说明是只读 computed
  if (isFunction(getterOrOptions)) {
    // getter就是传入的函数,setter根据环境开发环境打印警告,生产环境是空函数
    getter = getterOrOptions
    setter = __DEV__
      ? () => {
          console.warn('Write operation failed: computed value is readonly')
        }
      : NOOP
  } else {
    // 不是方法说明是自定义的 getter setter 
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }
  // 创建cRef即ComputedRef并返回
  const cRef = new ComputedRefImpl(
    getter,
    setter,
    isFunction(getterOrOptions) || !getterOrOptions.set
  )

  if (__DEV__ && debugOptions) {
    cRef.effect.onTrack = debugOptions.onTrack
    cRef.effect.onTrigger = debugOptions.onTrigger
  }

  return cRef as any
}

export const NOOP = () => {}
export const isFunction = (val: unknown): val is Function =>
  typeof val === 'function'

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

  private _value!: T
  private _dirty = true
  public readonly effect: ReactiveEffect<T>

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

  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean
  ) {
    // 创建 effect, 我们在看 effect 源码时知道了传入 lazy 代表不会立即执行,computed 表明 computed 上游依赖改变的时候,会优先 trigger runner effect, scheduler 表示 effect trigger 的时候会调用 scheduler 而不是直接调用 effect
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        // 在触发更新时把dirty置为true, 不会立即更新
        this._dirty = true
        triggerRefValue(this)
      }
    })
    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) {
    // 若_dirty为true则触发操作函数
      self._dirty = false
      self._value = self.effect.run()!
    }
    // 返回值
    return self._value
  }

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

export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
 // ref转换成响应式代理的普通对象,触发操作函数
  ref = toRaw(ref)
  if (ref.dep) {
    if (__DEV__) {
      triggerEffects(ref.dep, {
        target: ref,
        type: TriggerOpTypes.SET,
        key: 'value',
        newValue: newVal
      })
    } else {
      triggerEffects(ref.dep)
    }
  }
}

export function triggerEffects(
  dep: Dep | ReactiveEffect[],
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  // spread into array for stabilization
  for (const effect of isArray(dep) ? dep : [...dep]) {
    if (effect !== activeEffect || effect.allowRecurse) {
      if (__DEV__ && effect.onTrigger) {
        effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))
      }
      if (effect.scheduler) {
        effect.scheduler()
      } else {
        effect.run()
      }
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值