import { effect, ReactiveEffect, trigger, track } from './effect' import { TriggerOpTypes, TrackOpTypes } from './operations' import { Ref } 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 { 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 trigger(toRaw(this), TriggerOpTypes.SET, 'value') } } }) this[ReactiveFlags.IS_READONLY] = isReadonly } get value() { 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) } } 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 }