apiLifecycle.ts 3.3 KB

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