import { DebuggerOptions, ReactiveEffect } from './effect' import { Ref, trackRefValue, triggerRefValue } from './ref' import { isFunction, NOOP } from '@vue/shared' import { ReactiveFlags, toRaw } from './reactive' import { Dep } from './dep' declare const ComoutedRefSymbol: unique symbol export interface ComputedRef extends WritableComputedRef { readonly value: T [ComoutedRefSymbol]: true } export interface WritableComputedRef extends Ref { readonly effect: ReactiveEffect } export type ComputedGetter = (...args: any[]) => T export type ComputedSetter = (v: T) => void export interface WritableComputedOptions { get: ComputedGetter set: ComputedSetter } class ComputedRefImpl { public dep?: Dep = 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 = new ReactiveEffect(getter, () => { 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) trackRefValue(self) if (self._dirty) { self._dirty = false self._value = self.effect.run()! } return self._value } set value(newValue: T) { this._setter(newValue) } } export function computed( getter: ComputedGetter, debugOptions?: DebuggerOptions ): ComputedRef export function computed( options: WritableComputedOptions, debugOptions?: DebuggerOptions ): WritableComputedRef export function computed( getterOrOptions: ComputedGetter | WritableComputedOptions, debugOptions?: DebuggerOptions ) { let getter: ComputedGetter let setter: ComputedSetter const onlyGetter = isFunction(getterOrOptions) if (onlyGetter) { getter = getterOrOptions setter = __DEV__ ? () => { console.warn('Write operation failed: computed value is readonly') } : NOOP } else { 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 }