首先先看看如何捕获异常。
js异常的特点是,出现不会导致JS引擎崩溃 最多只会终止当前执行的任务。比如一个页面有两个按钮,如果点击按钮发生异常页面,这个时候页面不会崩溃,只是这个按钮的功能失效,其他按钮还会有效。
setTimeout(() => {
console.log('1->begin')
error
console.log('1->end')
})
setTimeout(() => {
console.log('2->begin')
console.log('2->end')
})
复制代码
上面的例子我们用setTimeout分别启动了两个任务,虽然第一个任务执行了一个错误的方法。程序执行停止了。但是另外一个任务并没有收到影响。
其实如果你不打开控制台都看不到发生了错误。好像是错误是在静默中发生的。
下面我们来看看这样的错误该如何收集。
我们首先想到的使用try-catch来收集。
setTimeout(() => {
try {
console.log('1->begin')
error
console.log('1->end')
} catch (e) {
console.log('catch',e)
}
})
复制代码
如果在函数中错误没有被捕获,错误会上抛。
function fun1() {
console.log('1->begin')
error
console.log('1->end')
}
setTimeout(() => {
try {
fun1()
} catch (e) {
console.log('catch',e)
}
})
复制代码
控制台中打印出的分别是错误信息和错误堆栈。
读到这里大家可能会想那就在最底层做一个错误try-catch不就好了吗。但是理想很丰满,现实很骨感。我们看看下一个例子。
function fun1() {
console.log('1->begin')
error
console.log('1->end')
}
try {
setTimeout(() => {
fun1()
})
} catch (e) {
console.log('catch', e)
}
复制代码
大家注意运行结果,异常并没有被捕获。
这是因为JS的try-catch功能非常有限一遇到异步就不好用了。那总不能为了收集错误给所有的异步都加一个try-catch吧,太坑爹了。
window.onerror 最大的好处就是可以同步任务还是异步任务都可捕获。
function fun1() {
console.log('1->begin')
error
console.log('1->end')
}
window.onerror = (...args) => {
console.log('onerror:',args)
}
setTimeout(() => {
fun1()
})
复制代码
onerror返回值
onerror还有一个问题大家要注意 如果返回返回true 就不会被上抛了。不然控制台中还会看到错误日志。
window.addEventListener('error',() => {})
其实onerror固然好但是还是有一类异常无法捕获。这就是网络异常的错误。比如下面的例子。
复制代码
试想一下我们如果页面上要显示的图片突然不显示了,而我们浑然不知那就是麻烦了。
addEventListener就是
window.addEventListener('error', args => {
console.log(
'error event:', args
);
return true;
},
true // 利用捕获方式
);
复制代码
Promise的出现主要是为了让我们解决回调地域问题。基本是我们程序开发的标配了。虽然我们提倡使用es7 async/await语法来写,但是不排除很多祖传代码还是存在Promise写法。
new Promise((resolve, reject) => {
abcxxx()
});
复制代码
这种情况无论是onerror还是监听错误事件都是无法捕获的
new Promise((resolve, reject) => {
error()
})
// 增加异常捕获
.catch((err) => {
console.log('promise catch:',err)
});
复制代码
除非每个Promise都添加一个catch方法。但是显然是不能这样做。
window.addEventListener("unhandledrejection", e => {
console.log('unhandledrejection',e)
});
复制代码
我们可以考虑将unhandledrejection事件捕获错误抛出交由错误事件统一处理就可以了
window.addEventListener("unhandledrejection", e => {
throw e.reason
});
复制代码
const asyncFunc = () => new Promise(resolve => {
error
})
setTimeout(async() => {
try {
await asyncFun()
} catch (e) {
console.log('catch:',e)
}
})
复制代码
实际上async/await语法本质还是Promise语法。区别就是async方法可以被上层的try/catch捕获。
如果不去捕获的话就会和Promise一样,需要用unhandledrejection事件捕获。这样的话我们只需要在全局增加unhandlerejection就好了。
异常类型 | 同步方法 | 异步方法 | 资源加载 | Promise | async/await |
---|---|---|---|---|---|
try/catch | ✔️ | ✔️ | |||
onerror | ✔️ | ✔️ | |||
error事件监听 | ✔️ | ✔️ | ✔️ | ||
unhandledrejection事件监听 | ✔️ | ✔️ |
实际上我们可以将unhandledrejection事件抛出的异常再次抛出就可以统一通过error事件进行处理了。
最终用代码表示如下:
window.addEventListener("unhandledrejection", e => {
throw e.reason
});
window.addEventListener('error', args => {
console.log(
'error event:', args
);
return true;
}, true);
复制代码
现在是前端工程化的时代,工程化导出的代码一般都是被压缩混淆后的。
比如:
setTimeout(() => {
xxx(1223)
}, 1000)
复制代码
出错的代码指向被压缩后的JS文件。
如果想将错误和原有的代码关联起来就需要sourcemap文件的帮忙了。
简单说, sourceMap
就是一个文件,里面储存着位置信息。
仔细点说,这个文件里保存的,是转换后代码的位置,和对应的转换前的位置。
那么如何利用sourceMap对还原异常代码发生的位置这个问题我们到异常分析这个章节再讲。
利用vue-cli工具直接创建一个项目。
# 安装vue-cli
npm install -g @vue/cli
# 创建一个项目
vue create vue-sample
cd vue-sample
npm i
// 启动应用
npm run serve
复制代码
为了测试的需要我们暂时关闭eslint 这里面还是建议大家全程打开eslint
在vue.config.js进行配置
module.exports = {
// 关闭eslint规则
devServer: {
overlay: {
warnings: true,
errors: true
}
},
lintOnSave:false
}
复制代码
我们故意在src/components/HelloWorld.vue