vue3-core/packages/runtime-core/src/errorHandling.ts

97 lines
2.7 KiB
TypeScript
Raw Normal View History

2018-10-09 23:37:24 +08:00
import { ComponentInstance } from './component'
2018-10-29 00:09:38 +08:00
import { warn, pushWarningContext, popWarningContext } from './warning'
import { VNode } from './vdom'
import { VNodeFlags } from './flags'
2018-09-19 23:35:38 +08:00
export const enum ErrorTypes {
2018-09-24 08:59:19 +08:00
BEFORE_CREATE = 1,
CREATED,
BEFORE_MOUNT,
MOUNTED,
BEFORE_UPDATE,
UPDATED,
BEFORE_DESTROY,
DESTROYED,
ERROR_CAPTURED,
2018-09-24 08:30:26 +08:00
RENDER,
2018-09-24 08:59:19 +08:00
WATCH_CALLBACK,
2018-09-24 08:30:26 +08:00
NATIVE_EVENT_HANDLER,
2018-10-29 00:09:38 +08:00
COMPONENT_EVENT_HANDLER,
SCHEDULER
2018-09-19 23:35:38 +08:00
}
2018-09-24 08:59:19 +08:00
const ErrorTypeStrings: Record<number, string> = {
[ErrorTypes.BEFORE_CREATE]: 'in beforeCreate lifecycle hook',
[ErrorTypes.CREATED]: 'in created lifecycle hook',
[ErrorTypes.BEFORE_MOUNT]: 'in beforeMount lifecycle hook',
[ErrorTypes.MOUNTED]: 'in mounted lifecycle hook',
[ErrorTypes.BEFORE_UPDATE]: 'in beforeUpdate lifecycle hook',
[ErrorTypes.UPDATED]: 'in updated lifecycle hook',
[ErrorTypes.BEFORE_DESTROY]: 'in beforeDestroy lifecycle hook',
[ErrorTypes.DESTROYED]: 'in destroyed lifecycle hook',
[ErrorTypes.ERROR_CAPTURED]: 'in errorCaptured lifecycle hook',
[ErrorTypes.RENDER]: 'in render function',
[ErrorTypes.WATCH_CALLBACK]: 'in watcher callback',
[ErrorTypes.NATIVE_EVENT_HANDLER]: 'in native event handler',
[ErrorTypes.COMPONENT_EVENT_HANDLER]: 'in component event handler',
[ErrorTypes.SCHEDULER]:
'when flushing updates. This may be a Vue internals bug.'
2018-09-19 23:35:38 +08:00
}
export function handleError(
err: Error,
instance: ComponentInstance | VNode,
2018-09-24 08:30:26 +08:00
type: ErrorTypes
2018-09-19 23:35:38 +08:00
) {
const isFunctional = (instance as VNode)._isVNode
2018-10-29 00:09:38 +08:00
const contextVNode = (isFunctional
? instance
: (instance as ComponentInstance).$parentVNode) as VNode | null
let cur: ComponentInstance | null = null
if (isFunctional) {
let vnode = instance as VNode | null
while (vnode && !(vnode.flags & VNodeFlags.COMPONENT_STATEFUL)) {
vnode = vnode.contextVNode
}
if (vnode) {
cur = vnode.children as ComponentInstance
}
} else {
cur = (instance as ComponentInstance).$parent
}
while (cur) {
2018-09-24 08:59:19 +08:00
const handler = cur.errorCaptured
if (handler) {
try {
const captured = handler.call(
cur,
err,
type,
2018-10-29 00:09:38 +08:00
isFunctional ? null : instance
)
2018-09-24 08:59:19 +08:00
if (captured) return
} catch (err2) {
2018-10-29 00:09:38 +08:00
logError(err2, ErrorTypes.ERROR_CAPTURED, contextVNode)
2018-09-24 08:59:19 +08:00
}
}
cur = cur.$parent
2018-09-24 08:59:19 +08:00
}
2018-10-29 00:09:38 +08:00
logError(err, type, contextVNode)
2018-09-24 08:59:19 +08:00
}
2018-10-29 00:09:38 +08:00
function logError(err: Error, type: ErrorTypes, contextVNode: VNode | null) {
2018-09-24 08:59:19 +08:00
if (__DEV__) {
const info = ErrorTypeStrings[type]
2018-10-29 00:09:38 +08:00
if (contextVNode) {
pushWarningContext(contextVNode)
}
warn(`Unhandled error${info ? ` ${info}` : ``}`)
2018-10-29 00:09:38 +08:00
if (contextVNode) {
popWarningContext()
}
2018-10-04 01:00:13 +08:00
console.error(err)
2018-09-24 08:59:19 +08:00
} else {
throw err
}
2018-09-19 23:35:38 +08:00
}