renderWatch.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import {
  2. type BaseWatchErrorCodes,
  3. type BaseWatchMiddleware,
  4. type BaseWatchOptions,
  5. baseWatch,
  6. } from '@vue/reactivity'
  7. import { NOOP, extend, invokeArrayFns, remove } from '@vue/shared'
  8. import {
  9. type ComponentInternalInstance,
  10. getCurrentInstance,
  11. setCurrentInstance,
  12. } from './component'
  13. import {
  14. createVaporRenderingScheduler,
  15. queuePostRenderEffect,
  16. } from './scheduler'
  17. import { handleError as handleErrorWithInstance } from './errorHandling'
  18. import { warn } from './warning'
  19. import { invokeDirectiveHook } from './directive'
  20. interface RenderWatchOptions {
  21. immediate?: boolean
  22. deep?: boolean
  23. once?: boolean
  24. }
  25. type WatchStopHandle = () => void
  26. export function renderEffect(effect: () => void): WatchStopHandle {
  27. return doWatch(effect)
  28. }
  29. export function renderWatch(
  30. source: any,
  31. cb: (value: any, oldValue: any) => void,
  32. options?: RenderWatchOptions,
  33. ): WatchStopHandle {
  34. return doWatch(source as any, cb, options)
  35. }
  36. function doWatch(
  37. source: any,
  38. cb?: any,
  39. options?: RenderWatchOptions,
  40. ): WatchStopHandle {
  41. const extendOptions: BaseWatchOptions =
  42. cb && options ? extend({}, options) : {}
  43. if (__DEV__) extendOptions.onWarn = warn
  44. // TODO: SSR
  45. // if (__SSR__) {}
  46. const instance = getCurrentInstance()
  47. extend(extendOptions, {
  48. onError: (err: unknown, type: BaseWatchErrorCodes) =>
  49. handleErrorWithInstance(err, instance, type),
  50. scheduler: createVaporRenderingScheduler(instance),
  51. middleware: createMiddleware(instance),
  52. })
  53. let effect = baseWatch(source, cb, extendOptions)
  54. const unwatch = !effect
  55. ? NOOP
  56. : () => {
  57. effect!.stop()
  58. if (instance && instance.scope) {
  59. remove(instance.scope.effects!, effect)
  60. }
  61. }
  62. return unwatch
  63. }
  64. const createMiddleware =
  65. (instance: ComponentInternalInstance | null): BaseWatchMiddleware =>
  66. next => {
  67. let value: unknown
  68. // with lifecycle
  69. if (instance && instance.isMounted) {
  70. const { bu, u, dirs } = instance
  71. // beforeUpdate hook
  72. const isFirstEffect = !instance.isUpdating
  73. if (isFirstEffect) {
  74. if (bu) {
  75. invokeArrayFns(bu)
  76. }
  77. if (dirs) {
  78. invokeDirectiveHook(instance, 'beforeUpdate')
  79. }
  80. instance.isUpdating = true
  81. }
  82. const reset = setCurrentInstance(instance)
  83. // run callback
  84. value = next()
  85. reset()
  86. if (isFirstEffect) {
  87. queuePostRenderEffect(() => {
  88. instance.isUpdating = false
  89. if (dirs) {
  90. invokeDirectiveHook(instance, 'updated')
  91. }
  92. // updated hook
  93. if (u) {
  94. queuePostRenderEffect(u)
  95. }
  96. })
  97. }
  98. } else {
  99. // is not mounted
  100. value = next()
  101. }
  102. return value
  103. }