What is computed

Computed is a computed property of a component, and it means state that is generated depending on other states.

Methods of use of computed tomography

Computed can be created in two ways

  1. tocomputedThe function passes agetterMethod to create an immutable Reactive ref object
const count = ref(1)

const plusOne = computed(() = > count.value + 1)

console.log(plusOne.value) / / 2

plusOne.value = 3 // error because plusOne is immutable ref obj
Copy the code
  1. tocomputedThe function passes a havegetsetMethod 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
Copy the code

The principle of computed cache values

Developers familiar with Vue know that computed values can be cached (to improve performance) and recalculated only when computed dependencies change, otherwise computed values remain the same. So how does computed do this? Why does computed know that dependencies have changed and need to be recalculated? What exactly happens inside Vue when you create a computed? Let’s take a look.

Create a Computed

To create computed, you need to call a computed function, which takes a getter method or an object containing a GET method and a set method and returns a ref object.

Here is the full code for the computed factory function:

export function computed<T> (getter: ComputedGetter<T>) :ComputedRef<T>
export function computed<T> (
  options: WritableComputedOptions<T>
) :WritableComputedRef<T>
export 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
  }

  return newComputedRefImpl( getter, setter, isFunction(getterOrOptions) || ! getterOrOptions.set )as any
}
Copy the code

Generate the REF object

In the code above we see that Vue creates a Ref object by executing the constructor ComputedRefImpl, and we now know that a REF object returned by a computed function is actually an instance created by executing the constructor ComputedRefImpl. So the ability to compute cached values must be in the internal logic of ComputedRefImpl, so let’s move on.

Here is the complete code for ComputedRefImpl:

class ComputedRefImpl<T> {
  private_value! : Tprivate _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
  ) {
    this.effect = effect(getter, {
      lazy: true.scheduler: () = > {
        if (!this._dirty) {
          this._dirty = true
          trigger(toRaw(this), TriggerOpTypes.SET, 'value')}}})this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    // the computed ref may get wrapped by other proxies e.g. readonly() #3376
    const self = toRaw(this)
    if (self._dirty) {
      self._value = this.effect()
      self._dirty = false
    }
    track(self, TrackOpTypes.GET, 'value')
    return self._value
  }

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

Let’s look at what the ComputedRefImpl constructor does. The constructor does two things:

  1. calleffectMethod to generatewatcherListen for the effect property of the function and assign it to the instancereactivity/effect.ts)
  2. Sets whether the ref object isreadonly

But the constructor doesn’t seem to have anything to do with computed functionality, so don’t worry, but let’s move on.

The execution timing of getter methods

We found that declaring a computed object does not actually execute its getter method, but only executes its getter method when reading a computed value, so we need to focus on the getter method for ComputedRefImpl.

The getter method is executed when computed values are read, and in the getter method there is a variable called _dirty, which means the dirty data switch. By default, _dirty is set to true, and in the getter method, the switch is turned on. You need to compute the computed value once, then turn off the switch, and then get the computed value again without recalculating because _dirty is false. This is how computed cache values work.

Computed recalculation of values

So how does computed know to recalculate? It’s as simple as setting _dirty to true somewhere and recalculating it when you get a computed value. So where is the operation to set _dirty to true? That’s right, in the constructor. The effect function generates listener functions for the responsive object of the object and sets the scheduler up. Remember the getter method we passed in when we created computed, which is what happens when the state of computed internal dependencies changes. So the final flow is this: when the state of computed internal dependencies changes, the corresponding listener functions are executed, which naturally perform the operations in the Scheduler. The scheduler sets _dirty to true.

And one of the questions that might arise from looking at this is, how does computed know that internal dependence is changing? This is because internal dependencies are accessed the first time we get a computed value (that is, execute a getter method), and dependencies are collected on them at that time, so computed knows that internal dependencies have changed.