computed.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { effect, ReactiveEffect, activeEffect } from './effect'
  2. import { Ref, UnwrapRef } from './ref'
  3. import { isFunction, NOOP } from '@vue/shared'
  4. export interface ComputedRef<T = any> extends WritableComputedRef<T> {
  5. readonly value: UnwrapRef<T>
  6. }
  7. export interface WritableComputedRef<T> extends Ref<T> {
  8. readonly effect: ReactiveEffect<T>
  9. }
  10. export type ComputedGetter<T> = () => T
  11. export type ComputedSetter<T> = (v: T) => void
  12. export interface WritableComputedOptions<T> {
  13. get: ComputedGetter<T>
  14. set: ComputedSetter<T>
  15. }
  16. export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
  17. export function computed<T>(
  18. options: WritableComputedOptions<T>
  19. ): WritableComputedRef<T>
  20. export function computed<T>(
  21. getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
  22. ) {
  23. let getter: ComputedGetter<T>
  24. let setter: ComputedSetter<T>
  25. if (isFunction(getterOrOptions)) {
  26. getter = getterOrOptions
  27. setter = __DEV__
  28. ? () => {
  29. console.warn('Write operation failed: computed value is readonly')
  30. }
  31. : NOOP
  32. } else {
  33. getter = getterOrOptions.get
  34. setter = getterOrOptions.set
  35. }
  36. let dirty = true
  37. let value: T
  38. const runner = effect(getter, {
  39. lazy: true,
  40. // mark effect as computed so that it gets priority during trigger
  41. computed: true,
  42. scheduler: () => {
  43. dirty = true
  44. }
  45. })
  46. return {
  47. _isRef: true,
  48. // expose effect so computed can be stopped
  49. effect: runner,
  50. get value() {
  51. if (dirty) {
  52. value = runner()
  53. dirty = false
  54. }
  55. // When computed effects are accessed in a parent effect, the parent
  56. // should track all the dependencies the computed property has tracked.
  57. // This should also apply for chained computed properties.
  58. trackChildRun(runner)
  59. return value
  60. },
  61. set value(newValue: T) {
  62. setter(newValue)
  63. }
  64. } as any
  65. }
  66. function trackChildRun(childRunner: ReactiveEffect) {
  67. if (activeEffect === undefined) {
  68. return
  69. }
  70. for (let i = 0; i < childRunner.deps.length; i++) {
  71. const dep = childRunner.deps[i]
  72. if (!dep.has(activeEffect)) {
  73. dep.add(activeEffect)
  74. activeEffect.deps.push(dep)
  75. }
  76. }
  77. }