VUE 3.0源码之computed

1. 入口

computed方法首先调用_computed实现计算属性,然后调用recordInstanceBoundEffect记录当前的effect,方便当组件卸载时清除

import { computed as _computed } from '@vue/reactivity'

function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
  const c = _computed(getterOrOptions as any)
  recordInstanceBoundEffect(c.effect)
  return c
}
2. 实现

首先收集getter和setter,然后调用effect方法生成一个响应式功能的函数,最后返回一个有get valueset value的对象。

// @vue/reactivity/computed
function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>

  if (isFunction(getterOrOptions)) {
    getter = getterOrOptions
    setter = __DEV__
      ? () => {
          console.warn('Write operation failed: computed value is readonly')
        }
        // 空函数
      : NOOP
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }

  let dirty = true
  let value: T
  let computed: ComputedRef<T>

  // 生成一个effect用于依赖收集和注册
  const runner = effect(getter, {
    lazy: true,
    computed: true,
    scheduler: () => {
      if (!dirty) {
        dirty = true
        // 触发相关依赖,通知计算属性更新,这样也可以重新触发get value
        trigger(computed, TriggerOpTypes.SET, 'value')
      }
    }
  })
  computed = {
    _isRef: true,
    effect: runner,
    get value() {
      // 通过dirty控制是否需要重新运行getter
      if (dirty) {
        value = runner()
        dirty = false
      }
      // 收集依赖
      track(computed, TrackOpTypes.GET, 'value')
      return value
    },
    set value(newValue: T) {
      setter(newValue)
    }
  } as any
  return computed
}
3. 原理

计算属性在注册时是不会触发getter的,这样可以避免没必要的性能损失。

get value

当触发get value时且dirty为true时触发effect生成的runner,这时会调用getter方法并触发getter方法中的响应式变量的getter实现依赖注册,接着生成新值并将dirty设为false,最后调用track方法收集依赖。

更新

当getter中的响应式数据变更时,会触发调用effect方法时传入的scheduler,这时会把dirty设为true并调用trigger方法通知相关依赖,这时依赖会触发get value

value

多加一层value是因为当计算属性直接返回一个如字符串等类型的值的时候,该计算属性的内部逻辑就会丢失。

你可能感兴趣的:(vue3,源码,vue,vue)