feat: basic error handling

This commit is contained in:
Evan You 2018-09-23 20:59:19 -04:00
parent 44d1a8efcb
commit 6ce39b4d20
2 changed files with 53 additions and 9 deletions

View File

@ -9,6 +9,7 @@ import {
import { setupWatcher } from './componentWatch' import { setupWatcher } from './componentWatch'
import { Autorun, DebuggerEvent, ComputedGetter } from '@vue/observer' import { Autorun, DebuggerEvent, ComputedGetter } from '@vue/observer'
import { nextTick } from '@vue/scheduler' import { nextTick } from '@vue/scheduler'
import { ErrorTypes } from './errorHandling'
type Flatten<T> = { [K in keyof T]: T[K] } type Flatten<T> = { [K in keyof T]: T[K] }
@ -44,6 +45,7 @@ export interface MountedComponent<D = Data, P = Data> extends Component {
updated?(): void updated?(): void
beforeDestroy?(): void beforeDestroy?(): void
destroyed?(): void destroyed?(): void
errorCaptured?(): (err: Error, type: ErrorTypes) => boolean | void
_updateHandle: Autorun _updateHandle: Autorun
_queueJob: ((fn: () => void) => void) _queueJob: ((fn: () => void) => void)

View File

@ -1,20 +1,37 @@
import { MountedComponent } from './component' import { MountedComponent } from './component'
export const enum ErrorTypes { export const enum ErrorTypes {
LIFECYCLE = 1, BEFORE_CREATE = 1,
CREATED,
BEFORE_MOUNT,
MOUNTED,
BEFORE_UPDATE,
UPDATED,
BEFORE_DESTROY,
DESTROYED,
ERROR_CAPTURED,
RENDER, RENDER,
RENDER_ERROR, RENDER_ERROR,
WATCH_CALLBACK,
NATIVE_EVENT_HANDLER, NATIVE_EVENT_HANDLER,
COMPONENT_EVENT_HANDLER COMPONENT_EVENT_HANDLER
} }
const globalHandlers: Function[] = [] const ErrorTypeStrings: Record<number, string> = {
[ErrorTypes.BEFORE_CREATE]: 'beforeCreate lifecycle hook',
export function globalHandleError(handler: () => void) { [ErrorTypes.CREATED]: 'created lifecycle hook',
globalHandlers.push(handler) [ErrorTypes.BEFORE_MOUNT]: 'beforeMount lifecycle hook',
return () => { [ErrorTypes.MOUNTED]: 'mounted lifecycle hook',
globalHandlers.splice(globalHandlers.indexOf(handler), 1) [ErrorTypes.BEFORE_UPDATE]: 'beforeUpdate lifecycle hook',
} [ErrorTypes.UPDATED]: 'updated lifecycle hook',
[ErrorTypes.BEFORE_DESTROY]: 'beforeDestroy lifecycle hook',
[ErrorTypes.DESTROYED]: 'destroyed lifecycle hook',
[ErrorTypes.ERROR_CAPTURED]: 'errorCaptured lifecycle hook',
[ErrorTypes.RENDER]: 'render function',
[ErrorTypes.RENDER_ERROR]: 'renderError function',
[ErrorTypes.WATCH_CALLBACK]: 'watcher callback',
[ErrorTypes.NATIVE_EVENT_HANDLER]: 'native event handler',
[ErrorTypes.COMPONENT_EVENT_HANDLER]: 'component event handler'
} }
export function handleError( export function handleError(
@ -22,5 +39,30 @@ export function handleError(
instance: MountedComponent, instance: MountedComponent,
type: ErrorTypes type: ErrorTypes
) { ) {
// TODO let cur = instance
while (cur.$parent) {
cur = cur.$parent
const handler = cur.errorCaptured
if (handler) {
try {
const captured = handler.call(cur, err, type)
if (captured) return
} catch (err2) {
logError(err2, cur, ErrorTypes.ERROR_CAPTURED)
}
}
}
logError(err, instance, type)
}
function logError(err: Error, instance: MountedComponent, type: ErrorTypes) {
if (__DEV__) {
const info = ErrorTypeStrings[type]
console.warn(
`Unhandled error${info ? ` in ${info}` : ``}: "${err.toString()}"`,
instance
)
} else {
throw err
}
} }