usage

There are two ways to use it: get only, whose return value is also read-only, and read and write

  1. Get: Passes a getter function. The return value of computed is read-only after the value is wrapped by ref.
const count = ref(1)
const plusOne = computed(() = > count.value + 1)

console.log(plusOne.value) / / 2

plusOne.value++ // error
Copy the code
  1. Set: You can also pass in an object with two functions, get and set, in which case the computed return value is writable.

Source of readable writable judgment is judged according to our incoming parameters, we can see, the is a function of the incoming if default is getter function, nature is a readable, and if the incoming object, note that here is not to function that is writable, you also have a set function, it also has a withdrawal in the source code:

// ...

 return newComputedRefImpl( getter, setter, isFunction(getterOrOptions) || ! getterOrOptions.set// This corresponds to the constructor's readonly
  ) as any


constructor(getter: ComputedGetter
       
        , private readonly _setter: ComputedSetter
        
         , isReadonly: boolean
        
       ) {
   / /...
  }
Copy the code

Type verification is performed with function overloading for two usages:

// read-only
function computed<T> (getter: () => T) :Readonly<Ref<Readonly<T>>>

// writable
function computed<T> (options: { get: () => T; set: (value: T) => void }) :Ref<T>
Copy the code

Watch and watchEffect are similar. Let’s take a closer look at the ComputedRefImpl class that returns this Ref

ComputedRefImpl

First post the source code:

class ComputedRefImpl<T> {
 // some initial var...

  constructor(getter: ComputedGetter
       
        , private readonly _setter: ComputedSetter
        
         , isReadonly: boolean
        
       ) {
   // .. 
  }

  get value() {
   // ...
  }

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

In addition to constructors and get and set, there are also calls to effect, track, trigger methods, the source code is specifically placed in a file effect. Ts, which is specialized in dealing with side effects, including the corresponding dependency storage

This call to effect wraps around the getter to create a cache, in the words of the document: calculated properties are cached based on their reactive dependencies, and they are reevaluated only when the associated reactive dependencies change

 this.effect = effect(getter, {
      lazy: true.scheduler: () = > {
        if (!this._dirty) {
          this._dirty = true
          trigger(toRaw(this), TriggerOpTypes.SET, 'value')}}Copy the code

There are two configuration items passed in: a lazy and a scheduler. I don’t know what the scheduler is at first, but it translates to a scheduler. A scheduler specifies how to run side effects functions, and there is a trigger function inside that triggers SET operations. So, as the name implies, the set operation is performed, and the _dirty variable is used to control whether the cache is read or the new value

get value() {
    if (this._dirty) {
      this._value = this.effect()
      this._dirty = false
    }
    track(toRaw(this), TrackOpTypes.GET, 'value')
    return this._value
  }
Copy the code

Then we go to the effect method, which is created using the createReativeEffect function

function createReactiveEffect<T = any> (fn: () => T, options: ReactiveEffectOptions) :ReactiveEffect<T> {
  const effect = function reactiveEffect() :unknown {
  / /...
  } asReactiveEffect effect.id = uid++ effect.allowRecurse = !! options.allowRecurse effect._isEffect =true
  effect.active = true
  effect.raw = fn
  effect.deps = []
  effect.options = options
  return effect
}
Copy the code

We can see that a reactiveEffect function is returned with the following properties:

  _isEffect: true
  id: number
  active: boolean
  raw: () = > T
  deps: Array<Dep>
  options: ReactiveEffectOptions
  allowRecurse: boolean
Copy the code

track & trigger

The data structure of the side effects dependencies looks like this:

weakmap:(target, Map(key,Set[effect,effect]) )

target—–>key——>dep

Track is used to track collection dependencies and trigger is used to trigger responses.