apiLifecycle.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import {
  2. type GenericComponentInstance,
  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: GenericComponentInstance | 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. // disable tracking inside all lifecycle hooks
  33. // since they can potentially be called inside effects.
  34. pauseTracking()
  35. // Set currentInstance during hook invocation.
  36. // This assumes the hook does not synchronously trigger other hooks, which
  37. // can only be false when the user does something really funky.
  38. const reset = setCurrentInstance(target)
  39. const res = callWithAsyncErrorHandling(hook, target, type, args)
  40. reset()
  41. resetTracking()
  42. return res
  43. })
  44. if (prepend) {
  45. hooks.unshift(wrappedHook)
  46. } else {
  47. hooks.push(wrappedHook)
  48. }
  49. return wrappedHook
  50. } else if (__DEV__) {
  51. const apiName = toHandlerKey(ErrorTypeStrings[type].replace(/ hook$/, ''))
  52. warn(
  53. `${apiName} is called when there is no active component instance to be ` +
  54. `associated with. ` +
  55. `Lifecycle injection APIs can only be used during execution of setup().` +
  56. (__FEATURE_SUSPENSE__
  57. ? ` If you are using async setup(), make sure to register lifecycle ` +
  58. `hooks before the first await statement.`
  59. : ``),
  60. )
  61. }
  62. }
  63. const createHook =
  64. <T extends Function = () => any>(lifecycle: LifecycleHooks) =>
  65. (
  66. hook: T,
  67. target: GenericComponentInstance | null = currentInstance,
  68. ): void => {
  69. // post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
  70. if (
  71. !isInSSRComponentSetup ||
  72. lifecycle === LifecycleHooks.SERVER_PREFETCH
  73. ) {
  74. injectHook(lifecycle, (...args: unknown[]) => hook(...args), target)
  75. }
  76. }
  77. type CreateHook<T = any> = (
  78. hook: T,
  79. target?: GenericComponentInstance | null,
  80. ) => void
  81. export const onBeforeMount: CreateHook = createHook(LifecycleHooks.BEFORE_MOUNT)
  82. export const onMounted: CreateHook = createHook(LifecycleHooks.MOUNTED)
  83. export const onBeforeUpdate: CreateHook = createHook(
  84. LifecycleHooks.BEFORE_UPDATE,
  85. )
  86. export const onUpdated: CreateHook = createHook(LifecycleHooks.UPDATED)
  87. export const onBeforeUnmount: CreateHook = createHook(
  88. LifecycleHooks.BEFORE_UNMOUNT,
  89. )
  90. export const onUnmounted: CreateHook = createHook(LifecycleHooks.UNMOUNTED)
  91. export const onServerPrefetch: CreateHook = createHook(
  92. LifecycleHooks.SERVER_PREFETCH,
  93. )
  94. export type DebuggerHook = (e: DebuggerEvent) => void
  95. export const onRenderTriggered: CreateHook<DebuggerHook> =
  96. createHook<DebuggerHook>(LifecycleHooks.RENDER_TRIGGERED)
  97. export const onRenderTracked: CreateHook<DebuggerHook> =
  98. createHook<DebuggerHook>(LifecycleHooks.RENDER_TRACKED)
  99. export type ErrorCapturedHook<TError = unknown> = (
  100. err: TError,
  101. instance: ComponentPublicInstance | null,
  102. info: string,
  103. ) => boolean | void
  104. export function onErrorCaptured<TError = Error>(
  105. hook: ErrorCapturedHook<TError>,
  106. target: GenericComponentInstance | null = currentInstance,
  107. ): void {
  108. injectHook(LifecycleHooks.ERROR_CAPTURED, hook, target)
  109. }