[Vue3 source code learning] responsive api: computed

Basic use of computed

computedIt is a computed property of a component, which means a state generated depending on other states, and is closely related to responsiveness.

computedThere are two ways to create:

  1. Pass a method to computedthe function getterto createimmutable reactive ref object

    const count = ref(1)
    const plusOne = computed(() => count.value + 1)
    console.log(plusOne.value) // 2
    plusOne.value = 3 // error,因为plusOne是immutable ref obj
    
  2. computedPass an object with getand setmethods to the function to create a writable ref object

    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 principle

computedThe characteristic of is that it can cache the calculated value (improving performance), and computedit will only be recalculated when the dependency of changes, otherwise the read computedvalue will always be the previous value. computedHow did you do it? Let's take a look at the source code to clarify.

computed source code

Source address:packages\reactivity\src\computed.ts

computed create

computedThe function takes a gettermethod or an object with getmethods and setmethods , and returns a refobject.

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>

  const onlyGetter = isFunction(getterOrOptions)
  // 当getterOrOptions为函数的时候,说明是只读computed,会将其赋值给与getter
  if (onlyGetter) {
    
    
    getter = getterOrOptions
    setter = __DEV__
    ? () => {
    
    
      console.warn('Write operation failed: computed value is readonly')
    }
    : NOOP
  } else {
    
    
    // 当getterOrOptions为对象的时候,说明是是自定义的getter setter,会将set和get分别赋值给setter,getter。
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }

  const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter)

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

ComputedRefImpl generates ref objects

From the above code, we can see that computedthe ref object returned by the function is an instance created ComputedRefImplby . ComputedRefImplThe construction method does two things in total:

  • Call effectthe method to generate watchera listener function and assign it to the effect property of the instance.
  • Sets whether the ref object is readonly.
class ComputedRefImpl<T> {
    
    
  public dep?: Dep = undefined
  private _value!: T
  private _dirty = true  // 脏数据flag 用来判断是否需要重新计算
  public readonly effect: ReactiveEffect<T> 

  public readonly __v_isRef = true // ref响应式对象标识
  public readonly [ReactiveFlags.IS_READONLY]: boolean // ReactiveFlags只读标识

  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean
  ) {
    
    
    // 调用 ReactiveEffect 方法生成监听函数并赋值给实例的 effect 属性
    this.effect = new ReactiveEffect(getter, () => {
    
    
      // 由于初始化时,计算过一次computed值,_dirty已经设为了false
      // 所以当内部依赖发生变化时,会由此进入,设置_dirty为true,这样获取computed值时会再次计算
      if (!this._dirty) {
    
    
        this._dirty = true
        triggerRefValue(this)
      }
    })
    // 设置ref对象是否为 readonly
    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) // 依赖收集
    // 初始化时,_dirty为true,会计算一次computed值
    if (self._dirty) {
    
    
      // 设置_dirty为false, 防止再次获取时重新计算,这就是 computed 缓存值的实现原理
      self._dirty = false
      self._value = self.effect.run()!
    }
    return self._value
  }

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

computedImplementation Analysis of Cache Value

We found that the getter method is not actually executed computedwhen , computedits gettermethod is read, and this method is implemented in the methodComputedRefImpl definition of the constructor.getter

getterThe method will be executed when reading computedthe value (including dependent collection). Due to the switch of dirty data, _dirtyit will be set to true) at the time of initialization. In getterthe method computedthe value of will be calculated and set self._dirty = false. After the data source does not change When computedretrieving the value of is will not be recalculated since is_dirty is . falseThis is how computedcached values ​​are implemented.

computed recomputed value

When the data source changes, the function ComputedRefImpladded to the object in the constructor effectwill generate a listener function for the responsive object of the object, and schedulerset .
Therefore, when computedthe state of the internal dependencies changes, the corresponding monitoring function is executed, which will naturally execute the operations schedulerin . And in schedulerthe _dirtyis set to true. Therefore, it will be recalculated the next time the value is taken.

epilogue

If this article is of any help to you, please give it a thumbs up to support it, thank you

Supongo que te gusta

Origin blog.csdn.net/qq_38987146/article/details/123199598
Recomendado
Clasificación