apiLifecycle.ts 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import {
  2. ComponentInternalInstance,
  3. LifecycleHooks,
  4. currentInstance,
  5. setCurrentInstance,
  6. isInSSRComponentSetup
  7. } from './component'
  8. import { ComponentPublicInstance } from './componentProxy'
  9. import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling'
  10. import { warn } from './warning'
  11. import { capitalize } from '@vue/shared'
  12. import { pauseTracking, resetTracking, DebuggerEvent } from '@vue/reactivity'
  13. export { onActivated, onDeactivated } from './components/KeepAlive'
  14. export function injectHook(
  15. type: LifecycleHooks,
  16. hook: Function & { __weh?: Function },
  17. target: ComponentInternalInstance | null = currentInstance,
  18. prepend: boolean = false
  19. ) {
  20. if (target) {
  21. const hooks = target[type] || (target[type] = [])
  22. // cache the error handling wrapper for injected hooks so the same hook
  23. // can be properly deduped by the scheduler. "__weh" stands for "with error
  24. // handling".
  25. const wrappedHook =
  26. hook.__weh ||
  27. (hook.__weh = (...args: unknown[]) => {
  28. if (target.isUnmounted) {
  29. return
  30. }
  31. // disable tracking inside all lifecycle hooks
  32. // since they can potentially be called inside effects.
  33. pauseTracking()
  34. // Set currentInstance during hook invocation.
  35. // This assumes the hook does not synchronously trigger other hooks, which
  36. // can only be false when the user does something really funky.
  37. setCurrentInstance(target)
  38. const res = callWithAsyncErrorHandling(hook, target, type, args)
  39. setCurrentInstance(null)
  40. resetTracking()
  41. return res
  42. })
  43. if (prepend) {
  44. hooks.unshift(wrappedHook)
  45. } else {
  46. hooks.push(wrappedHook)
  47. }
  48. } else if (__DEV__) {
  49. const apiName = `on${capitalize(
  50. ErrorTypeStrings[type].replace(/ hook$/, '')
  51. )}`
  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 = <T extends Function = () => any>(
  64. lifecycle: LifecycleHooks
  65. ) => (hook: T, target: ComponentInternalInstance | null = currentInstance) =>
  66. // post-create lifecycle registrations are noops during SSR
  67. !isInSSRComponentSetup && injectHook(lifecycle, hook, target)
  68. export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
  69. export const onMounted = createHook(LifecycleHooks.MOUNTED)
  70. export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
  71. export const onUpdated = createHook(LifecycleHooks.UPDATED)
  72. export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
  73. export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)
  74. export type DebuggerHook = (e: DebuggerEvent) => void
  75. export const onRenderTriggered = createHook<DebuggerHook>(
  76. LifecycleHooks.RENDER_TRIGGERED
  77. )
  78. export const onRenderTracked = createHook<DebuggerHook>(
  79. LifecycleHooks.RENDER_TRACKED
  80. )
  81. export type ErrorCapturedHook = (
  82. err: Error,
  83. instance: ComponentPublicInstance | null,
  84. info: string
  85. ) => boolean | void
  86. export const onErrorCaptured = (
  87. hook: ErrorCapturedHook,
  88. target: ComponentInternalInstance | null = currentInstance
  89. ) => {
  90. injectHook(LifecycleHooks.ERROR_CAPTURED, hook, target)
  91. }