解决异样的意义
随着网页我的项目越来越简单,许多异样报错很难在开发和测试阶段被发现,只管你可能避开了语法等惯例谬误,但不可避免的是代码在运行时的谬误你仍旧无奈精确意料,假如当初有如下一段 Vue
代码,它在生命周期的 created
阶段异步申请并接管了谬误的数据,可能就会导致页面渲染呈现谬误:
<template> {{ test.obj.xxx }}</template>......created() { this.getSomeData()},methods: { getSomeData() { this.fetch().then((res) => { this.test = res // 假如这是申请的谬误数据 }) },}
而如果测试人员及时发现了这一谬误的话,当他关上控制台时往往就会立刻下结论了:噢,是前端的锅
事实上真正的我的项目中可能会遇到更多"微妙"的问题,而且如果谬误仅产生在某些用户端,那将无从觉察,于是咱们会想到应该在程序中解决捕捉运行时谬误,将谬误上报至服务器,而后剖析和改良代码来修复曾经产生的谬误。
所以该如何应答并解决可能产生的某些谬误,成为了前端开发的一门必修课,你当然能够在每个代码片段中反复编写 try...catch...
、为每个 Promise
都解决 catch
,但这未免显得有些狼狈,于是我思考能不能用更优雅的形式,对立解决所有异样,将谬误在全局进行捕捉而后上报剖析。其实在 Vue 中实现这样全局的异样解决并不难,上面看看我是如何做的吧。
如何全局捕捉谬误异样
查问 Vue 文档咱们能够发现全局配置中就有这么一个捕捉谬误的解决钩子 errorHandler,用法很简略:
Vue.config.errorHandler = function (err, vm, info) { // `info` 是 Vue 特定的错误信息,比方谬误所在的生命周期钩子 // 只在 2.2.0+ 可用 }
只须要用这个钩子就能够解决大部分 Vue 利用中的谬误(如组件生命周期中的谬误、自定义事件处理函数外部谬误、v-on
DOM 监听器外部抛出的谬误),并且回调中自带的 info
参数也标记了这个谬误大略是属于哪类,同时它还能解决返回 Promise
链的谬误,能够说是十分弱小了,然而它也并非能解决所有的异样,否则文章写到这就该完结了 ~ 接下来咱们测试一下。
首先在全局谬误捕捉中输入一下 log,先运行一下结尾的申请数据谬误例子:
Vue.config.errorHandler = function (err, vm, info) { console.log('vue异样谬误捕捉: ', '谬误产生在 ' + info)}
能够看到异样胜利被捕捉了,因为咱们模仿了一个数据谬误导致渲染出错,所以谬误产生在 render
层,如果是在函数中的 Promise
产生的谬误呢?咱们试一下:
async created() { await this.getSomeData()},method: { async getSomeData() { const res = await this.fetch() this.test = res }, fetch() { asdasd = 1 // 这里给一个未定义的变量赋值,必定会报错 return new Promise((resolve) => { // ......省略 }) }
没有问题,接下来咱们再试试一个按钮用 v-on
绑定 click
,然而成心在办法内制作谬误,看看是什么成果:
<button @click="doSomeThing"> Test </button>..........doSomeThing() { aaaaaaaa = 111111 // 这里给一个未定义的变量赋值,必定会报错},
看来事件也能失常捕捉,咱们再试试写一个组件,在组件中自定义一个事件,看看后果如何:
<my-custom-comp @node-click="doSomeThing" />// 在组件中是 $emit 触发:this.$emit('node-click', item)
这个异样仍旧是被胜利捕捉了,当然生命周期钩子中的谬误异样也都能胜利捕捉,就不多做演示了,到目前为止都没有什么问题,然而如果谬误不产生在 Vue 外部呢?
<button onclick="foo()">bad button</button>
能够看到这个异样没有被顺利捕捉,同样的,如果是内部 JS 代码报错,也都是无奈捕捉的,也就是说这个钩子只能捕捉与 Vue 相关联的事件。
宏工作中的谬误也是无奈捕捉的:
.......fetch() { return new Promise((resolve) => { setTimeout(() => { asd = 1 // 在宏工作的异步中呈现的谬误 resolve({}) }, 1000) })},
如果 Promise
异样未被失常解决的话,也是捕捉不到的,如下代码,留神这里 create
没有用 await
形式调用异步办法:
created() { this.getSomeData();},methods: { async getSomeData() { await (asdasd = 1); },},
上面咱们就逐个解决这两个场景下的谬误捕捉问题。
解决 JS 的额定谬误
咱们能够用 BOM 提供的全局谬误处理函数 window.onerror
来尝试捕捉,它接管多个参数:
window.onerror = function (message, source, line, column, error) { console.log('全局捕捉谬误', message, source, line, column, error)}
出错代码:
<button onclick="foo()">bad button</button>
当初 JS 异样谬误都能够被捕捉到了,包含 setTimeout
宏工作的异步谬误也能够被捕捉,但咱们留神到未被失常解决的 Promise
谬误仍不能胜利捕捉。
解决 Promise 谬误
参考 Vue 中 error.js
的代码,同步工作异样捕捉就是套上一层 try...catch...
,这也解释了为什么 Vue 捕捉的谬误不会被全局 window.onerror
再次捕捉,因为曾经在这里抛出了。而异步工作异样解决则是判断如果是 Promise
则把 catch
指向错误处理中:
咱们能够模拟写一个插件,来解决 Vue 实例中 methods
的异样。
function isPromise(ret) { return ret && typeof ret.then === 'function' && typeof ret.catch === 'function'}const handleMethods = (instance) => { if (instance.$options.methods) { let actions = instance.$options.methods || {} for (const key in actions) { if (Object.hasOwnProperty.call(actions, key)) { let fn = actions[key] actions[key] = function (...args) { let ret = args.length > 0 ? fn.apply(this, args) : fn.call(this) if (isPromise(ret) && !ret._handled) { ret._handled = true return ret.catch((e) => errorHandler(e, this, `捕捉到了未解决的Promise异样: (Promise/async)`)) } } } } }}export default { install: (Vue, options) => { Vue.mixin({ beforeCreate() { this.$route.meta.capture && handleMethods(this) }, }) },}
因为遍历所有办法可能会造成页面性能损失,所以这里我加了一个条件,须要在路由设置
meta
能力开启该组件下的method
额定异样捕捉。
再试试下面的错误代码,看看后果:
created() { this.getSomeData();},methods: { async getSomeData() { await (asdasd = 1); },},
能够被失常捕捉,这种形式的益处是咱们能够把产生谬误的实例信息传进去,如果不想应用这种办法,或是在 Vue3 中应用 setup
形式而不是 options
写法,还能够应用全局的事件监听来捕捉:
window.addEventListener('unhandledrejection', (event) => { console.log('全局捕捉未解决的Promise异样', event)})
残缺代码
errorPlugin.js
function errorHandler(err, vm, info) { console.log('vue异样谬误捕捉: ', '错误信息 ' + info) // TODO: 处理错误上报}const handleMethods = (instance) => { if (instance.$options.methods) { let actions = instance.$options.methods || {} for (const key in actions) { if (Object.hasOwnProperty.call(actions, key)) { let fn = actions[key] actions[key] = function (...args) { let ret = args.length > 0 ? fn.apply(this, args) : fn.call(this) if (isPromise(ret) && !ret._handled) { ret._handled = true return ret.catch((e) => errorHandler(e, this, `捕捉到了未解决的Promise异样: (Promise/async)`)) } } } } }}function isPromise(ret) { return ret && typeof ret.then === 'function' && typeof ret.catch === 'function'}let GlobalError = { install: (Vue, options) => { Vue.config.errorHandler = errorHandler // eslint-disable-next-line max-params window.onerror = function (message, source, line, column, error) { errorHandler(message, null, '全局捕捉谬误') // console.log('全局捕捉谬误', message, source, line, column, error) } window.addEventListener('unhandledrejection', (event) => { errorHandler(event, null, '全局捕捉未解决的Promise异样') }) Vue.mixin({ beforeCreate() { this.$route.meta.capture && handleMethods(this) }, }) },}export default GlobalError
main.js 中引入
import Vue from 'vue'import App from './App.vue'import router from './router'Vue.config.productionTip = false// 引入错误处理插件import ErrorPlugin from './errorPlugin'Vue.use(ErrorPlugin)new Vue({ router, render: (h) => h(App),}).$mount('#app')
在 Vue3 中应用
一样在 main.js 中引入插件即可:
import { createApp } from 'vue'import App from './App.vue'import router from './router'import ErrorPlugin from './errorPlugin'createApp(App).use(router).use(ErrorPlugin).mount('#app')
完结
如果你须要更加丰盛的谬误收集剖析性能,还是得应用如 Sentry 、 Bugsnag 这类欠缺的谬误追踪服务,不过绝对来讲这些都须要不少配置部署操作。本文介绍了如何简略地在 Vue 中全局捕捉异样谬误,晋升代码健壮性,且能防止在代码中编写大量异样捕捉块,同时也缩小了出错时控制台的大片飘红报警,收集谬误能够帮忙咱们定位开发与测试阶段不易发现的疑难杂症,这部分能够应用 http
申请将错误信息发送到服务器。
以上就是文章的全部内容,心愿对你有所帮忙!如果感觉文章写的不错,能够点赞珍藏,也欢送关注,我会继续更新更多前端有用的常识与实用技巧,我是茶无味de一天,心愿与你独特成长~