共计 5265 个字符,预计需要花费 14 分钟才能阅读完成。
解决异样的意义
随着网页我的项目越来越简单,许多异样报错很难在开发和测试阶段被发现,只管你可能避开了语法等惯例谬误,但不可避免的是代码在运行时的谬误你仍旧无奈精确意料,假如当初有如下一段 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 一天,心愿与你独特成长~