From 6ce39b4d2069a74e00175aba3670f71b9be13cc7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 23 Sep 2018 20:59:19 -0400 Subject: [PATCH] feat: basic error handling --- packages/core/src/component.ts | 2 + packages/core/src/errorHandling.ts | 60 +++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 3ac07bde4..a40a3488d 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -9,6 +9,7 @@ import { import { setupWatcher } from './componentWatch' import { Autorun, DebuggerEvent, ComputedGetter } from '@vue/observer' import { nextTick } from '@vue/scheduler' +import { ErrorTypes } from './errorHandling' type Flatten = { [K in keyof T]: T[K] } @@ -44,6 +45,7 @@ export interface MountedComponent extends Component { updated?(): void beforeDestroy?(): void destroyed?(): void + errorCaptured?(): (err: Error, type: ErrorTypes) => boolean | void _updateHandle: Autorun _queueJob: ((fn: () => void) => void) diff --git a/packages/core/src/errorHandling.ts b/packages/core/src/errorHandling.ts index 8a59d2ab0..464f628ec 100644 --- a/packages/core/src/errorHandling.ts +++ b/packages/core/src/errorHandling.ts @@ -1,20 +1,37 @@ import { MountedComponent } from './component' export const enum ErrorTypes { - LIFECYCLE = 1, + BEFORE_CREATE = 1, + CREATED, + BEFORE_MOUNT, + MOUNTED, + BEFORE_UPDATE, + UPDATED, + BEFORE_DESTROY, + DESTROYED, + ERROR_CAPTURED, RENDER, RENDER_ERROR, + WATCH_CALLBACK, NATIVE_EVENT_HANDLER, COMPONENT_EVENT_HANDLER } -const globalHandlers: Function[] = [] - -export function globalHandleError(handler: () => void) { - globalHandlers.push(handler) - return () => { - globalHandlers.splice(globalHandlers.indexOf(handler), 1) - } +const ErrorTypeStrings: Record = { + [ErrorTypes.BEFORE_CREATE]: 'beforeCreate lifecycle hook', + [ErrorTypes.CREATED]: 'created lifecycle hook', + [ErrorTypes.BEFORE_MOUNT]: 'beforeMount lifecycle hook', + [ErrorTypes.MOUNTED]: 'mounted lifecycle hook', + [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( @@ -22,5 +39,30 @@ export function handleError( instance: MountedComponent, 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 + } }