Vue 的错误处理机制
2020/3/20 11:01:56
本文主要是介绍Vue 的错误处理机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
任何一个框架,对于错误的处理都是一种必备的能力。在 Vue 中,则是定义了一套对应的错误处理规则给到使用者。且在源代码级别,对部分必要的过程做了一定的错误处理。
全局设置错误处理
在 Vue 全局设置的 API 中,我们可以设置全局错误处理函数,用法如下:
Vue.config.errorHandler = function (err, vm, info) { // handle error // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子 // 只在 2.2.0+ 可用 } 复制代码
该函数可以作为指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和 Vue 实例。
如果我们想要针对自己的应用,对错误做统一的收集与处理(如上报后台系统),那么该 API 是一个极好的嵌入点。
不过值得注意的是,在不同 Vue 版本中,该全局 API 作用的范围会有所不同:
从 2.2.0 起,这个钩子也会捕获组件生命周期钩子里的错误。同样的,当这个钩子是
undefined
时,被捕获的错误会通过console.error
输出而避免应用崩溃。
从 2.4.0 起,这个钩子也会捕获 Vue 自定义事件处理函数内部的错误了。
从 2.6.0 起,这个钩子也会捕获
v-on
DOM 监听器内部抛出的错误。另外,如果任何被覆盖的钩子或处理函数返回一个 Promise 链 (例如 async 函数),则来自其 Promise 链的错误也会被处理。
生命周期钩子 errorCaptured
这是 2.5.0 新增的一个生命钩子函数。可以点击这里查看详细。
它被调用的时机在于:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false
以阻止该错误继续向上传播。
错误传播规则
参考官网的解释:
- 默认情况下,如果全局的
config.errorHandler
被定义,所有的错误仍会发送它,因此这些错误仍然会向单一的分析服务的地方进行汇报。 - 如果一个组件的继承或父级从属链路中存在多个
errorCaptured
钩子,则它们将会被相同的错误逐个唤起。 - 如果此
errorCaptured
钩子自身抛出了一个错误,则这个新错误和原本被捕获的错误都会发送给全局的config.errorHandler
。 - 一个
errorCaptured
钩子能够返回false
以阻止错误继续向上传播。本质上是说“这个错误已经被搞定了且应该被忽略”。它会阻止其它任何会被这个错误唤起的errorCaptured
钩子和全局的config.errorHandler
。
内置的错误处理函数
在 Vue 2.6.10 的源码中,文件src/core/util/error.js
中定义了对于 Vue 内部自身使用的几个错误处理函数。针对同步异常与异步异常,有不同处理方式。我们详细来看:
处理同步异常
处理同步异常的函数是 handleError(err: Error, vm: any, info: string)
。详细实现:
export function handleError (err: Error, vm: any, info: string) { // Deactivate deps tracking while processing error handler to avoid possible infinite rendering. // See: https://github.com/vuejs/vuex/issues/1505 pushTarget() try { if (vm) { let cur = vm while ((cur = cur.$parent)) { const hooks = cur.$options.errorCaptured if (hooks) { for (let i = 0; i < hooks.length; i++) { try { const capture = hooks[i].call(cur, err, vm, info) === false if (capture) return } catch (e) { globalHandleError(e, cur, 'errorCaptured hook') } } } } } globalHandleError(err, vm, info) } finally { popTarget() } } 复制代码
该代码对上面提到的“错误传播规则”做了实现。如果一个组件的继承或父级从属链路中存在多个 errorCaptured
钩子,则它们将会被相同的错误逐个唤起。 errorCaptured
钩子能够返回 false
以阻止错误继续向上传播。最后,通过调用 globalHandleError()
方法:
function globalHandleError (err, vm, info) { if (config.errorHandler) { try { return config.errorHandler.call(null, err, vm, info) } catch (e) { // if the user intentionally throws the original error in the handler, // do not log it twice if (e !== err) { logError(e, null, 'config.errorHandler') } } } logError(err, vm, info) } 复制代码
globalHandleError()
方法最终调用的是全局的 config.errorHandler()
方法。
处理异步异常
对于异步异常怎么处理呢?也好办,将异步处理的函数包裹一层,当异步处理函数在执行过程中出现错误的时候,将异常捕获并处理。具体的实现在invokeWithErrorHandling()
方法:
export function invokeWithErrorHandling ( handler: Function, context: any, args: null | any[], vm: any, info: string ) { let res try { res = args ? handler.apply(context, args) : handler.call(context) if (res && !res._isVue && isPromise(res) && !res._handled) { res.catch(e => handleError(e, vm, info + ` (Promise/async)`)) // issue #9511 // avoid catch triggering multiple times when nested calls res._handled = true } } catch (e) { handleError(e, vm, info) } return res } 复制代码
代码中,对包裹函数的返回是否是异步函数做了isPromise()
的判断:
export function isPromise (val: any): boolean { return ( isDef(val) && typeof val.then === 'function' && typeof val.catch === 'function' ) } 复制代码
符合异步函数的条件之后,将上文提到的handleError
写入到 promise.catch 中。
这样,就完成了对于异步函数的处理过程。
源码在何处使用了异步异常捕获
hook钩子函数
// src/core/instance/lifecycle.js export function callHook (vm: Component, hook: string) { // #7573 disable dep collection when invoking lifecycle hooks pushTarget() const handlers = vm.$options[hook] const info = `${hook} hook` if (handlers) { for (let i = 0, j = handlers.length; i < j; i++) { invokeWithErrorHandling(handlers[i], vm, null, vm, info) } } if (vm._hasHookEvent) { vm.$emit('hook:' + hook) } popTarget() } 复制代码
自定义事件的处理函数
//src/core/instance/events.js Vue.prototype.$emit = function (event: string): Component { const vm: Component = this let cbs = vm._events[event] if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs const args = toArray(arguments, 1) const info = `event handler for "${event}"` for (let i = 0, l = cbs.length; i < l; i++) { invokeWithErrorHandling(cbs[i], vm, args, vm, info) } } return vm } 复制代码
v-on 监听器
//src/core/vdom/helpers/update-listeners.js export function createFnInvoker (fns: Function | Array<Function>, vm: ?Component): Function { function invoker () { const fns = invoker.fns if (Array.isArray(fns)) { const cloned = fns.slice() for (let i = 0; i < cloned.length; i++) { invokeWithErrorHandling(cloned[i], null, arguments, vm, `v-on handler`) } } else { // return handler return value for single handlers return invokeWithErrorHandling(fns, null, arguments, vm, `v-on handler`) } } invoker.fns = fns return invoker } 复制代码
这几个地方,其实分别对应的就是一开头所提到的全局 API Vue.config.errorHandler 作用的范围。
总结
在这篇文章,我们了解了 Vue 的错误处理机制。章节内容不多,希望对于读者了解 Vue 内部的原理有一点帮助。
这篇关于Vue 的错误处理机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-04package.json 文件位置在哪?-icode9专业技术文章分享
- 2024-10-01Craco.js学习:从入门到实践指南
- 2024-10-01Create-React-App学习:入门与实践指南
- 2024-10-01CSS-in-JS学习:从入门到实践指南
- 2024-09-30JSX语法学习:从入门到初步掌握
- 2024-09-30Mock.js学习:入门教程与实战演练
- 2024-09-30React Hooks学习:从入门到实践
- 2024-09-30受控组件学习:React中的基础入门教程
- 2024-09-29JS定时器教程:初学者必看指南
- 2024-09-29JS对象教程:初学者的全面指南