持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
前言
前端最怕控制台“爆红”,特别是生产环境“爆红”,这样的话就会算作线上故障,可能导致一年白干;为了避免线上报错导致用户长时间的无法使用,我们需要设计一个错误告警系统,遇到错误能够及时上报给开发者,这样我们总能在第一时间修复bug,减少损失;但是首先我们需要搞清楚js中有哪些错误类型如何捕捉它们;
js中的错误类型就像是一种属性,都有与之对应的属性克制它(也就是对应的监听的事件)
window.onerror
负责监控js运行时错误
我们故意写一段错误代码,假设后端返回了一个null:
const backendData = null
const dataSource = backendData.list
复制代码
放到控制台执行一下:报错,这就是js语法错误 那么这种错误如何捕捉呢?使用window.onerror,window.onerror能够拿到我们控制台报错的大部分信息:报错信息(msg)、行数(lineNo)、列数(columnNo),根据行列我们可以准确定位问题
window.onerror=(msg,url,lineNo,columnNo,e)=>{
console.table({
msg, url, lineNo, columnNo, e
});
}
复制代码
我们再次运行上面的错误代码:错误信息先是以表格形式展示出来,然后爆红
如果非常讨厌控制台报错,可以这样做
window.onerror=(msg,url,lineNo,columnNo,e)=>{
return true
}
复制代码
可以看到错误消失,return true能够阻止事件向上抛出
除此之外它还可以捕捉异步报错
window.onerror = (msg,url,lineNo,columnNo,e)=>{
console.table({
msg, url, lineNo, columnNo, e
});
}
setTimeout(() => {
const obj = null
console.log(obj.name)
})
复制代码

然后我们把上面的同步错误代码用另一个server跑起来,然后在当前html引入,发现捕捉到的错误信息是这样的:
很伤心没有任何错误提示,只有一个Script error,对于我们来说没有任何提示作用,这里我们暂且放下不讲,等到后面的章节揭开谜底,我们继续看onerror还能捕捉哪些错误;
iframe错误
像上面一样我们把错误代码塞到一个html中然后ifame引入
居然没有捕捉到iframe的错误,只是看到控制台又恶心地爆红了;iframe的错误情况比较多,且听我细细道来;
iframe载入的脚本有以下几种情况:
- 载入与主页面同源的html,此时可以使用window.frames[0]拿到iframe的window,从而捕捉到错误信息
window.frames[0].onerror = (msg,url,lineNo,columnNo,e)=>{
console.table({
msg, url, lineNo, columnNo, e
});
return true
}
复制代码
- 主域名相同而子域名不同,可以设置domain相同,此时我们本地无法模拟
- 域名完全不同,错误不受控制
unhandledrejection
Promise被reject之后没有catch就会触发该错误
该错误的捕捉很简单:监听"unhandledrejection"事件就可以了
window.addEventListener('unhandledrejection',(e)=>{
if(isObject(e.reason)){
console.log('未处理的unhandledrejection事件')
} else{
console.log(e.reason)
}
})
复制代码
跨域脚本错误:Script error.
我们上面讲到了window.onerror无法捕捉到跨域脚本错误,怎么办呢?我们需要分多钟情况进行分析:
- 如果远程脚本内部运行时错误,还是报跨域脚本错误
<script>
window.onerror = console.log
</script>
<script src="http://127.0.0.1:8080/data.js" ></script>
复制代码
2. 如果远程脚本定义了一个函数,在我们当前页面执行,那么用try catch包裹之后可以捕捉到到错误信息 假设data.js暴露了一个函数load,我们在当前页面执行它
<script src="http://127.0.0.1:8080/data.js" ></script>
<script>
try {
load()
} catch (error) {
throw error
}
</script>
复制代码
3. 如果是事件中的回调函数报错,其实是一样的处理流程,就是需要用try catch包裹这个回调函数,但是这里我们需要用到AOP的思想,去重写addEventListener方法
const originAddEventListener = EventTarget.prototype.addEventListener
EventTarget.prototype.addEventListener = (type, listener, options) => {
const wrappedListener = (...args) => {
try {
return listener.apply(this, args)
} catch (err) {
throw err
}
}
return originAddEventListener.call(this, type, wrappedListener, options)
}
复制代码
总结
window.onerror负责捕获运行时错误;iframe错误则需要取到iframe中的window再去捕获;unhandledrejction则需要我们牢记“始于promise终于catch”;而跨域脚本错误则需要用try catch去包裹执行的函数体以捕捉错误;