computed.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import { DebuggerOptions, ReactiveEffect } from './effect'
  2. import { Ref, trackRefValue, triggerRefValue } from './ref'
  3. import { isFunction, NOOP } from '@vue/shared'
  4. import { ReactiveFlags, toRaw } from './reactive'
  5. import { Dep } from './dep'
  6. declare const ComputedRefSymbol: unique symbol
  7. export interface ComputedRef<T = any> extends WritableComputedRef<T> {
  8. readonly value: T
  9. [ComputedRefSymbol]: true
  10. }
  11. export interface WritableComputedRef<T> extends Ref<T> {
  12. readonly effect: ReactiveEffect<T>
  13. }
  14. export type ComputedGetter<T> = (...args: any[]) => T
  15. export type ComputedSetter<T> = (v: T) => void
  16. export interface WritableComputedOptions<T> {
  17. get: ComputedGetter<T>
  18. set: ComputedSetter<T>
  19. }
  20. export class ComputedRefImpl<T> {
  21. public dep?: Dep = undefined
  22. private _value!: T
  23. public readonly effect: ReactiveEffect<T>
  24. public readonly __v_isRef = true
  25. public readonly [ReactiveFlags.IS_READONLY]: boolean = false
  26. public _dirty = true
  27. public _cacheable: boolean
  28. constructor(
  29. getter: ComputedGetter<T>,
  30. private readonly _setter: ComputedSetter<T>,
  31. isReadonly: boolean,
  32. isSSR: boolean
  33. ) {
  34. this.effect = new ReactiveEffect(getter, () => {
  35. if (!this._dirty) {
  36. this._dirty = true
  37. triggerRefValue(this)
  38. }
  39. })
  40. this.effect.computed = this
  41. this.effect.active = this._cacheable = !isSSR
  42. this[ReactiveFlags.IS_READONLY] = isReadonly
  43. }
  44. get value() {
  45. // the computed ref may get wrapped by other proxies e.g. readonly() #3376
  46. const self = toRaw(this)
  47. trackRefValue(self)
  48. if (self._dirty || !self._cacheable) {
  49. self._dirty = false
  50. self._value = self.effect.run()!
  51. }
  52. return self._value
  53. }
  54. set value(newValue: T) {
  55. this._setter(newValue)
  56. }
  57. }
  58. /**
  59. * Takes a getter function and returns a readonly reactive ref object for the
  60. * returned value from the getter. It can also take an object with get and set
  61. * functions to create a writable ref object.
  62. *
  63. * @example
  64. * ```js
  65. * // Creating a readonly computed ref:
  66. * const count = ref(1)
  67. * const plusOne = computed(() => count.value + 1)
  68. *
  69. * console.log(plusOne.value) // 2
  70. * plusOne.value++ // error
  71. * ```
  72. *
  73. * ```js
  74. * // Creating a writable computed ref:
  75. * const count = ref(1)
  76. * const plusOne = computed({
  77. * get: () => count.value + 1,
  78. * set: (val) => {
  79. * count.value = val - 1
  80. * }
  81. * })
  82. *
  83. * plusOne.value = 1
  84. * console.log(count.value) // 0
  85. * ```
  86. *
  87. * @param getter - Function that produces the next value.
  88. * @param debugOptions - For debugging. See {@link https://vuejs.org/guide/extras/reactivity-in-depth.html#computed-debugging}.
  89. * @see {@link https://vuejs.org/api/reactivity-core.html#computed}
  90. */
  91. export function computed<T>(
  92. getter: ComputedGetter<T>,
  93. debugOptions?: DebuggerOptions
  94. ): ComputedRef<T>
  95. export function computed<T>(
  96. options: WritableComputedOptions<T>,
  97. debugOptions?: DebuggerOptions
  98. ): WritableComputedRef<T>
  99. export function computed<T>(
  100. getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
  101. debugOptions?: DebuggerOptions,
  102. isSSR = false
  103. ) {
  104. let getter: ComputedGetter<T>
  105. let setter: ComputedSetter<T>
  106. const onlyGetter = isFunction(getterOrOptions)
  107. if (onlyGetter) {
  108. getter = getterOrOptions
  109. setter = __DEV__
  110. ? () => {
  111. console.warn('Write operation failed: computed value is readonly')
  112. }
  113. : NOOP
  114. } else {
  115. getter = getterOrOptions.get
  116. setter = getterOrOptions.set
  117. }
  118. const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)
  119. if (__DEV__ && debugOptions && !isSSR) {
  120. cRef.effect.onTrack = debugOptions.onTrack
  121. cRef.effect.onTrigger = debugOptions.onTrigger
  122. }
  123. return cRef as any
  124. }