apiLifecycle.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import {
  2. type ComponentInternalInstance,
  3. currentInstance,
  4. isInSSRComponentSetup,
  5. setCurrentInstance,
  6. } from './component'
  7. import type { ComponentPublicInstance } from './componentPublicInstance'
  8. import { ErrorTypeStrings, callWithAsyncErrorHandling } from './errorHandling'
  9. import { warn } from './warning'
  10. import { toHandlerKey } from '@vue/shared'
  11. import {
  12. type DebuggerEvent,
  13. pauseTracking,
  14. resetTracking,
  15. } from '@vue/reactivity'
  16. import { LifecycleHooks } from './enums'
  17. export { onActivated, onDeactivated } from './components/KeepAlive'
  18. export function injectHook(
  19. type: LifecycleHooks,
  20. hook: Function & { __weh?: Function },
  21. target: ComponentInternalInstance | null = currentInstance,
  22. prepend: boolean = false,
  23. ): Function | undefined {
  24. if (target) {
  25. const hooks = target[type] || (target[type] = [])
  26. // cache the error handling wrapper for injected hooks so the same hook
  27. // can be properly deduped by the scheduler. "__weh" stands for "with error
  28. // handling".
  29. const wrappedHook =
  30. hook.__weh ||
  31. (hook.__weh = (...args: unknown[]) => {
  32. if (target.isUnmounted) {
  33. return
  34. }
  35. // disable tracking inside all lifecycle hooks
  36. // since they can potentially be called inside effects.
  37. pauseTracking()
  38. // Set currentInstance during hook invocation.
  39. // This assumes the hook does not synchronously trigger other hooks, which
  40. // can only be false when the user does something really funky.
  41. const reset = setCurrentInstance(target)
  42. const res = callWithAsyncErrorHandling(hook, target, type, args)
  43. reset()
  44. resetTracking()
  45. return res
  46. })
  47. if (prepend) {
  48. hooks.unshift(wrappedHook)
  49. } else {
  50. hooks.push(wrappedHook)
  51. }
  52. return wrappedHook
  53. } else if (__DEV__) {
  54. const apiName = toHandlerKey(ErrorTypeStrings[type].replace(/ hook$/, ''))
  55. warn(
  56. `${apiName} is called when there is no active component instance to be ` +
  57. `associated with. ` +
  58. `Lifecycle injection APIs can only be used during execution of setup().` +
  59. (__FEATURE_SUSPENSE__
  60. ? ` If you are using async setup(), make sure to register lifecycle ` +
  61. `hooks before the first await statement.`
  62. : ``),
  63. )
  64. }
  65. }
  66. export const createHook =
  67. <T extends Function = () => any>(lifecycle: LifecycleHooks) =>
  68. (hook: T, target: ComponentInternalInstance | null = currentInstance) =>
  69. // post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
  70. (!isInSSRComponentSetup || lifecycle === LifecycleHooks.SERVER_PREFETCH) &&
  71. injectHook(lifecycle, (...args: unknown[]) => hook(...args), target)
  72. export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
  73. export const onMounted = createHook(LifecycleHooks.MOUNTED)
  74. export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
  75. export const onUpdated = createHook(LifecycleHooks.UPDATED)
  76. export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
  77. export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)
  78. export const onServerPrefetch = createHook(LifecycleHooks.SERVER_PREFETCH)
  79. export type DebuggerHook = (e: DebuggerEvent) => void
  80. export const onRenderTriggered = createHook<DebuggerHook>(
  81. LifecycleHooks.RENDER_TRIGGERED,
  82. )
  83. export const onRenderTracked = createHook<DebuggerHook>(
  84. LifecycleHooks.RENDER_TRACKED,
  85. )
  86. export type ErrorCapturedHook<TError = unknown> = (
  87. err: TError,
  88. instance: ComponentPublicInstance | null,
  89. info: string,
  90. ) => boolean | void
  91. export function onErrorCaptured<TError = Error>(
  92. hook: ErrorCapturedHook<TError>,
  93. target: ComponentInternalInstance | null = currentInstance,
  94. ) {
  95. injectHook(LifecycleHooks.ERROR_CAPTURED, hook, target)
  96. }