Computed Declaration Method Method 1: Read-only

function computed<T> (getter: () => T) :Readonly<Ref<Readonly<T>>>
Copy the code

Such as:

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

console.log(plusOne.value) / / 2

plusOne.value++ // error

Copy the code

The source code

export function computed<T> (getter: ComputedGetter<T>) :ComputedRef<T>
Copy the code

Computed Declaration Method Method 2: Writable

export interface ComputedRef<T = any> extends WritableComputedRef<T> {
  readonly value: T
}

export interface WritableComputedRef<T> extends Ref<T> {
  readonly effect: ReactiveEffect<T>
}

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

Such as:

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 source code

export type ComputedGetter<T> = (ctx? : any) = > T
export type ComputedSetter<T> = (v: T) = > void

export interface WritableComputedOptions<T> {
  get: ComputedGetter<T>
  set: ComputedSetter<T>
}

class ComputedRefImpl<T> { 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  // Check whether the incoming is readable based on whether it is a function
  ) {
  // The effect function is called with lazy true
    this.effect = effect(getter, {
      lazy: true.scheduler: () = > {
      //set, the first time _dirty is false, because get changed, call trigger
      // Trigger the dependency
        if (!this._dirty) {
          this._dirty = true
          trigger(toRaw(this), TriggerOpTypes.SET, 'value')}}})this[ReactiveFlags.IS_READONLY] = isReadonly
  }
// this.effect() is equivalent to get() in vue2, collecting dependencies
// _dirty is for lazy Watchers
// in Vue2: watcher initialization immediately calls the watcher. Get method once, and then you can actually delay the execution of the watcher
  get value() {
  // By default _dirty is true and the data is dirty, so the first time it is triggered, effect() is called to get the value, then _dirty becomes false and track() is triggered
    if (this._dirty) {
      this._value = this.effect()
      this._dirty = false
    }
    track(toRaw(this), TrackOpTypes.GET, 'value')
    return this._value
  }

  set value(newValue: T) {
    this._setter(newValue)
  }
}
// If a function is passed in, it is only get, i.e. readable but not writable. Otherwise, it is an object with get and set
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

It is clear from the source that we use objects with get and set functions to create writable ref objects.

Get is the trigger track function; Set is the trigger function;