apiLifecycle.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import {
  2. ComponentInternalInstance,
  3. currentInstance,
  4. isInSSRComponentSetup,
  5. setCurrentInstance,
  6. unsetCurrentInstance
  7. } from './component'
  8. import { ComponentPublicInstance } from './componentPublicInstance'
  9. import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling'
  10. import { warn } from './warning'
  11. import { toHandlerKey } from '@vue/shared'
  12. import { DebuggerEvent, pauseTracking, resetTracking } from '@vue/reactivity'
  13. import { LifecycleHooks } from './enums'
  14. export { onActivated, onDeactivated } from './components/KeepAlive'
  15. export function injectHook(
  16. type: LifecycleHooks,
  17. hook: Function & { __weh?: Function },
  18. target: ComponentInternalInstance | null = currentInstance,
  19. prepend: boolean = false
  20. ): Function | undefined {
  21. if (target) {
  22. const hooks = target[type] || (target[type] = [])
  23. // cache the error handling wrapper for injected hooks so the same hook
  24. // can be properly deduped by the scheduler. "__weh" stands for "with error
  25. // handling".
  26. const wrappedHook =
  27. hook.__weh ||
  28. (hook.__weh = (...args: unknown[]) => {
  29. if (target.isUnmounted) {
  30. return
  31. }
  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. setCurrentInstance(target)
  39. const res = callWithAsyncErrorHandling(hook, target, type, args)
  40. unsetCurrentInstance()
  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. export const createHook =
  64. <T extends Function = () => any>(lifecycle: LifecycleHooks) =>
  65. (hook: T, target: ComponentInternalInstance | null = currentInstance) =>
  66. // post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
  67. (!isInSSRComponentSetup || lifecycle === LifecycleHooks.SERVER_PREFETCH) &&
  68. injectHook(lifecycle, (...args: unknown[]) => hook(...args), target)
  69. export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
  70. export const onMounted = createHook(LifecycleHooks.MOUNTED)
  71. export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
  72. export const onUpdated = createHook(LifecycleHooks.UPDATED)
  73. export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
  74. export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)
  75. export const onServerPrefetch = createHook(LifecycleHooks.SERVER_PREFETCH)
  76. export type DebuggerHook = (e: DebuggerEvent) => void
  77. export const onRenderTriggered = createHook<DebuggerHook>(
  78. LifecycleHooks.RENDER_TRIGGERED
  79. )
  80. export const onRenderTracked = createHook<DebuggerHook>(
  81. LifecycleHooks.RENDER_TRACKED
  82. )
  83. export type ErrorCapturedHook<TError = unknown> = (
  84. err: TError,
  85. instance: ComponentPublicInstance | null,
  86. info: string
  87. ) => boolean | void
  88. export function onErrorCaptured<TError = Error>(
  89. hook: ErrorCapturedHook<TError>,
  90. target: ComponentInternalInstance | null = currentInstance
  91. ) {
  92. injectHook(LifecycleHooks.ERROR_CAPTURED, hook, target)
  93. }