import { effect, ReactiveEffect } from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' import { isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' export interface ComputedRef extends WritableComputedRef { readonly value: T } export interface WritableComputedRef extends Ref { readonly effect: ReactiveEffect } export type ComputedGetter = (ctx?: any) => T export type ComputedSetter = (v: T) => void export interface WritableComputedOptions { get: ComputedGetter set: ComputedSetter } class ComputedRefImpl { public dep?: Set = undefined private _value!: T private _dirty = true public readonly effect: ReactiveEffect public readonly __v_isRef = true; public readonly [ReactiveFlags.IS_READONLY]: boolean constructor( getter: ComputedGetter, private readonly _setter: ComputedSetter, isReadonly: boolean ) { this.effect = effect(getter, { lazy: true, scheduler: () => { if (!this._dirty) { this._dirty = true triggerRefValue(this) } } }) 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 } trackRefValue(this) return self._value } set value(newValue: T) { this._setter(newValue) } } export function computed(getter: ComputedGetter): ComputedRef export function computed( options: WritableComputedOptions ): WritableComputedRef export function computed( getterOrOptions: ComputedGetter | WritableComputedOptions ) { let getter: ComputedGetter let setter: ComputedSetter 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 new ComputedRefImpl( getter, setter, isFunction(getterOrOptions) || !getterOrOptions.set ) as any }