errorHandling.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { VNode } from './vnode'
  2. import { ComponentInternalInstance, LifecycleHooks } from './component'
  3. import { warn, pushWarningContext, popWarningContext } from './warning'
  4. // contexts where user provided function may be executed, in addition to
  5. // lifecycle hooks.
  6. export const enum ErrorCodes {
  7. SETUP_FUNCTION = 1,
  8. RENDER_FUNCTION,
  9. WATCH_GETTER,
  10. WATCH_CALLBACK,
  11. WATCH_CLEANUP,
  12. NATIVE_EVENT_HANDLER,
  13. COMPONENT_EVENT_HANDLER,
  14. DIRECTIVE_HOOK,
  15. APP_ERROR_HANDLER,
  16. APP_WARN_HANDLER,
  17. SCHEDULER
  18. }
  19. export const ErrorTypeStrings: Record<number | string, string> = {
  20. [LifecycleHooks.BEFORE_CREATE]: 'beforeCreate hook',
  21. [LifecycleHooks.CREATED]: 'created hook',
  22. [LifecycleHooks.BEFORE_MOUNT]: 'beforeMount hook',
  23. [LifecycleHooks.MOUNTED]: 'mounted hook',
  24. [LifecycleHooks.BEFORE_UPDATE]: 'beforeUpdate hook',
  25. [LifecycleHooks.UPDATED]: 'updated',
  26. [LifecycleHooks.BEFORE_UNMOUNT]: 'beforeUnmount hook',
  27. [LifecycleHooks.UNMOUNTED]: 'unmounted hook',
  28. [LifecycleHooks.ACTIVATED]: 'activated hook',
  29. [LifecycleHooks.DEACTIVATED]: 'deactivated hook',
  30. [LifecycleHooks.ERROR_CAPTURED]: 'errorCaptured hook',
  31. [LifecycleHooks.RENDER_TRACKED]: 'renderTracked hook',
  32. [LifecycleHooks.RENDER_TRIGGERED]: 'renderTriggered hook',
  33. [ErrorCodes.SETUP_FUNCTION]: 'setup function',
  34. [ErrorCodes.RENDER_FUNCTION]: 'render function',
  35. [ErrorCodes.WATCH_GETTER]: 'watcher getter',
  36. [ErrorCodes.WATCH_CALLBACK]: 'watcher callback',
  37. [ErrorCodes.WATCH_CLEANUP]: 'watcher cleanup function',
  38. [ErrorCodes.NATIVE_EVENT_HANDLER]: 'native event handler',
  39. [ErrorCodes.COMPONENT_EVENT_HANDLER]: 'component event handler',
  40. [ErrorCodes.DIRECTIVE_HOOK]: 'directive hook',
  41. [ErrorCodes.APP_ERROR_HANDLER]: 'app errorHandler',
  42. [ErrorCodes.APP_WARN_HANDLER]: 'app warnHandler',
  43. [ErrorCodes.SCHEDULER]:
  44. 'scheduler flush. This may be a Vue internals bug. ' +
  45. 'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue'
  46. }
  47. export type ErrorTypes = LifecycleHooks | ErrorCodes
  48. export function callWithErrorHandling(
  49. fn: Function,
  50. instance: ComponentInternalInstance | null,
  51. type: ErrorTypes,
  52. args?: any[]
  53. ) {
  54. let res: any
  55. try {
  56. res = args ? fn(...args) : fn()
  57. } catch (err) {
  58. handleError(err, instance, type)
  59. }
  60. return res
  61. }
  62. export function callWithAsyncErrorHandling(
  63. fn: Function,
  64. instance: ComponentInternalInstance | null,
  65. type: ErrorTypes,
  66. args?: any[]
  67. ) {
  68. const res = callWithErrorHandling(fn, instance, type, args)
  69. if (res != null && !res._isVue && typeof res.then === 'function') {
  70. ;(res as Promise<any>).catch(err => {
  71. handleError(err, instance, type)
  72. })
  73. }
  74. return res
  75. }
  76. export function handleError(
  77. err: Error,
  78. instance: ComponentInternalInstance | null,
  79. type: ErrorTypes
  80. ) {
  81. const contextVNode = instance ? instance.vnode : null
  82. if (instance) {
  83. let cur: ComponentInternalInstance | null = instance.parent
  84. // the exposed instance is the render proxy to keep it consistent with 2.x
  85. const exposedInstance = instance.renderProxy
  86. // in production the hook receives only the error code
  87. const errorInfo = __DEV__ ? ErrorTypeStrings[type] : type
  88. while (cur) {
  89. const errorCapturedHooks = cur.ec
  90. if (errorCapturedHooks !== null) {
  91. for (let i = 0; i < errorCapturedHooks.length; i++) {
  92. if (errorCapturedHooks[i](err, exposedInstance, errorInfo)) {
  93. return
  94. }
  95. }
  96. }
  97. cur = cur.parent
  98. }
  99. // app-level handling
  100. const appErrorHandler = instance.appContext.config.errorHandler
  101. if (appErrorHandler) {
  102. callWithErrorHandling(
  103. appErrorHandler,
  104. null,
  105. ErrorCodes.APP_ERROR_HANDLER,
  106. [err, exposedInstance, errorInfo]
  107. )
  108. return
  109. }
  110. }
  111. logError(err, type, contextVNode)
  112. }
  113. function logError(err: Error, type: ErrorTypes, contextVNode: VNode | null) {
  114. // default behavior is crash in prod & test, recover in dev.
  115. // TODO we should probably make this configurable via `createApp`
  116. if (
  117. __DEV__ &&
  118. !(typeof process !== 'undefined' && process.env.NODE_ENV === 'test')
  119. ) {
  120. const info = ErrorTypeStrings[type]
  121. if (contextVNode) {
  122. pushWarningContext(contextVNode)
  123. }
  124. warn(`Unhandled error${info ? ` during execution of ${info}` : ``}`)
  125. console.error(err)
  126. if (contextVNode) {
  127. popWarningContext()
  128. }
  129. } else {
  130. throw err
  131. }
  132. }