vue3中的错误处理方案
概述
在设计一个框架时,如何处理错误是必不可少的环节。错误在程序开发中避无可避,那么出现错误时,至少要保证程序不会崩溃。合理的错误处理方案会大大增强框架的健壮性,覆盖异常场景,提高系统的可用性。
本文将探讨vue3
中的错误处理设计。
错误处理设计
vue3
中的错误处理逻辑集中在程序运行时,源码部分位于runtime-core\src\errorHandling.ts
。其内部封装了两个工具函数:callWithErrorHandling
和callWithAsyncErrorHandling
,用于处理函数调用中的错误,它们在vue3
的响应式系统和组件生命周期中扮演着重要角色,通常情况下只是vue3
内部调用。
ErrorCodes
和ErrorTypeStrings
类型
vue3
的错误处理定义了错误码和错误类型,用来标识错误,方便排查问题。
callWithErrorHandling
方法
callWithErrorHandling
函数用于同步函数调用时捕获错误,会在函数执行期间捕获并处理可能出现的异常情况或同步错误
其实现如下:
function callWithErrorHandling(fn, instance, type, args) {try {return args ? fn(...args) : fn();} catch (err) {handleError(err, instance, type);}
}
上述示例很好理解,就是用到try
、catch
取执行fn
函数,并返回fn
函数执行的结果;在catch
中捕获错误,调用handleError
函数
callWithAsyncErrorHandling
方法
callWithAsyncErrorHandling
函数用于处理异步函数调用中的错误,它能够捕捉和处理Promise
的reject
错误。
其实现如下
function callWithAsyncErrorHandling(fn, instance, type, args) {//fn:函数 instance:vue实例 type:类型 args:传递给fn的参数,下同if (isFunction(fn)) {const res = callWithErrorHandling(fn, instance, type, args);if (res && isPromise(res)) {res.catch((err) => {handleError(err, instance, type);});}return res;}if (isArray(fn)) {const values = [];for (let i = 0; i < fn.length; i++) {values.push(callWithAsyncErrorHandling(fn[i], instance, type, args));}return values;} else if (true) {warn2(`Invalid value type passed to callWithAsyncErrorHandling(): ${typeof fn}`);}
}
上面这段代码也不难理解,首先判断fn
是否是函数,若是函数,则调用callWithAsyncErrorHandling
,然后判断其返回值是否是Promise
,若是Promise
,则在其catch
方法中绑定handleError
方法;若fn
则是数组,则遍历fn
,循环调用callWithAsyncErrorHandling
;如果fn
既不是函数又不是数组,则调用warn2
,抛出异常。
handleError
方法
上述两个工具函数在捕获到错误后,都会调用handleError
处理错误,其作用就是找出错误的上下文,指明错误的地方,以及处理错误回调。
其实现如下:
function handleError(err, instance, type, throwInDev = true) {const contextVNode = instance ? instance.vnode : null;if (instance) {let cur = instance.parent;const exposedInstance = instance.proxy;const errorInfo = true? ErrorTypeStrings[type]: `https://vuejs.org/error-reference/#runtime-${type}`;while (cur) {const errorCapturedHooks = cur.ec;if (errorCapturedHooks) {for (let i = 0; i < errorCapturedHooks.length; i++) {if (errorCapturedHooks[i](err, exposedInstance, errorInfo) === false) {return;}}}cur = cur.parent;}const appErrorHandler = instance.appContext.config.errorHandler;if (appErrorHandler) {pauseTracking();callWithErrorHandling(appErrorHandler, null, 10 /* APP_ERROR_HANDLER */, [err,exposedInstance,errorInfo,]);resetTracking();return;}}logError(err, type, contextVNode, throwInDev);
}
handleError
方法接收四个参数err
, instance
, type
, throwInDev
。分别是错误信息、vue 实例、错误类型、是否在环境中抛出错误。默认都会抛出异常。若instance
存在,则判断当前节点的父节点,在父组件中去捕获并处理子组件的错误。下面会介绍onErrorCaptured
。
handleError
中还会判断全局变量上是否挂载了errorHandler
函数,如果系统的前端侧需要处理错误日志,监测前端错误的,就可以在main.js
这样写,
app.config.errorHandler = (p) => {const [err, exposedInstance, errorInfo] = p; //对应handleError中的变量
};
onErrorCaptured
cur.ec
实质上是获取组件的生命周期函数onErrorCaptured
,这个钩子函数主要用于捕获和处理其所有子组件的抛出的错误,以便在更高层级的组件中进行错误处理。
用法一般如下
errorCaptured(err, instance, info) {// err:处理错误 info:包含关于错误的额外信息,如发生在哪个生命周期钩子/*** 逻辑代码 */return false; // true:错误会继续传播 false:可以阻止错误继续向上抛出},