| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- import { ReactiveEffect } from './effect'
- import { Ref, trackRefValue, triggerRefValue } from './ref'
- import { isFunction, NOOP } from '@vue/shared'
- import { ReactiveFlags, toRaw } from './reactive'
- export interface ComputedRef<T = any> extends WritableComputedRef<T> {
- readonly value: T
- defer?: (fn: () => void) => void
- }
- export interface WritableComputedRef<T> extends Ref<T> {
- readonly effect: ReactiveEffect<T>
- }
- export type ComputedGetter<T> = (ctx?: any) => T
- export type ComputedSetter<T> = (v: T) => void
- export interface WritableComputedOptions<T> {
- get: ComputedGetter<T>
- set: ComputedSetter<T>
- }
- type ComputedScheduler = (fn: () => void) => void
- let scheduler: ComputedScheduler | undefined
- /**
- * Set a scheduler for deferring computed computations
- */
- export const setComputedScheduler = (s: ComputedScheduler | undefined) => {
- scheduler = s
- }
- class ComputedRefImpl<T> {
- public dep?: Set<ReactiveEffect> = undefined
- 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
- ) {
- let deferFn: () => void
- let scheduled = false
- this.effect = new ReactiveEffect(getter, () => {
- if (!this._dirty) {
- this._dirty = true
- if (scheduler) {
- if (!scheduled) {
- scheduled = true
- scheduler(
- deferFn ||
- (deferFn = () => {
- scheduled = false
- if (this._dirty) {
- this._dirty = false
- const newValue = this.effect.run()!
- if (this._value !== newValue) {
- this._value = newValue
- triggerRefValue(this)
- }
- } else {
- triggerRefValue(this)
- }
- })
- )
- }
- } else {
- 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 = self.effect.run()!
- self._dirty = false
- }
- trackRefValue(self)
- return self._value
- }
- set value(newValue: T) {
- this._setter(newValue)
- }
- }
- export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
- export function computed<T>(
- options: WritableComputedOptions<T>
- ): WritableComputedRef<T>
- 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 new ComputedRefImpl(
- getter,
- setter,
- isFunction(getterOrOptions) || !getterOrOptions.set
- ) as any
- }
|