computed.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  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 (__SSR__) {
  52. return getter()
  53. }
  54. if (dirty) {
  55. value = runner()
  56. dirty = false
  57. }
  58. // When computed effects are accessed in a parent effect, the parent
  59. // should track all the dependencies the computed property has tracked.
  60. // This should also apply for chained computed properties.
  61. trackChildRun(runner)
  62. return value
  63. },
  64. set value(newValue: T) {
  65. setter(newValue)
  66. }
  67. } as any
  68. }
  69. function trackChildRun(childRunner: ReactiveEffect) {
  70. if (activeEffect === undefined) {
  71. return
  72. }
  73. for (let i = 0; i < childRunner.deps.length; i++) {
  74. const dep = childRunner.deps[i]
  75. if (!dep.has(activeEffect)) {
  76. dep.add(activeEffect)
  77. activeEffect.deps.push(dep)
  78. }
  79. }
  80. }