这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战
稳定性是衡量一个项目的重要指标之一,若是一个项目的代码没有做到良好的错误收集和处理,一方面会导致极差的用户体验,另一方面也不利于项目的稳定运行
错误和异常的收集,也是前端领域一需要重点掌握的,接下来就让我们一起来学习吧
try catch
首先是家喻户晓的try catch,是一种能力有限且对于代码侵入性较强的方式
try{
// 代码块
}catch(err) {
// 错误处理
}
在我们写代码时,需要对预估有错误风险的代码使用try catch进行包裹
但是try catch并不是万能的,具有一定的局限性
-
无法捕获语法错误
try{ console.log(obj.) }catch(err) { // 错误处理 console.log('err: ',err) }此时控制台会报错:Uncaught SyntaxError: Unexpected token ')'
try catch 并没有对语法错误进行有效的捕获
-
无法捕获异步错误
try{ console.log(obj.name) }catch(err) { // 错误处理 console.log('err: ',err) }上述同步代码是可以被捕获的,若是加上一个定时器将其变成异步,如下述代码,便不能被有效的捕获
try{ setTimeout(() => { console.log(obj.name) }, 100) }catch(err) { // 错误处理 console.log('err: ',err) }Promise
Promise的错误捕获方式一般都是通过catch函数或者then的第二个参数onRejected进行捕获
// catch 方式 Promise.reject('data').then(res => { console.log('success: ', res) }).catch(err => { console.log('error: ', err) }) // 回调函数 onRejected 方式 Promise.reject('data').then(res => { console.log('success: ', res) }, err => { console.log('error: ', err) })对于 Promise 的错误处理,我们还可以注册对 Promise 全局异常的捕获事件 unhandledrejection:
window.addEventListener("unhandledrejection", err => { err.preventDefault() console.log(err.reason) return true })
onerror
我们再看一下 window.onerror 对错误进行处理的方案:开发者只需要给 window 添加 onerror 事件监听,同时注意需要将 window.onerror 放在所有脚本之前,这样才能对语法异常和运行异常进行处理。
/**
* mesage 为错误信息提示
* source 为错误脚本地址
* lineno 为错误的代码所在行号
* colno 为错误的代码所在列号
* error 为错误的对象信息,比如 error.stack 获取错误的堆栈信息
*/
window.onerror = (message, source, lineno, colno, error) => {
// ...
}
window.onerror 这种方式对代码侵入性较小,除了对语法错误和网络错误(因为网络请求异常不会事件冒泡)无能为力以外,无论是异步还是非异步,onerror 都能捕获到运行时错误。
但是需要注意的是,如果想使用 window.onerror 函数消化错误,需要显示返回 true,以保证错误不会向上抛出,控制台也就不会看到一堆错误提示。
window.addEventListener('error') 的能力和window.onerror除了DOM0级事件和DOM2事件的差异之外,还存在一些差异:
window.addEventListener('error') 方式对加载异常进行处理,注意这时候我们无法使用 window.onerror 进行处理,因为 window.onerror 事件是通过事件冒泡获取 error 信息的,而网络加载错误是不会进行事件冒泡的。
不支持冒泡的事件还有:鼠标聚焦 / 失焦(focus / blur)、鼠标移动相关事件(mouseleave / mouseenter)、一些 UI 事件(如 scroll、resize 等)。
window.addEventListener 不同于 window.onerror,它通过事件捕获获取 error 信息,从而可以对网络资源的加载异常进行处理:
window.addEventListener('error', err => {
console.log('err: ', err)
}, true)
那么,怎么区分网络资源加载错误和其他一般错误呢?这里有个小技巧,普通错误的 error 对象中会有一个 error.message 属性,表示错误信息,而资源加载错误对应的 error 对象却没有,因此可以根据下面代码进行判断:
window.addEventListener('error', error => {
if (!error.message) {
// 网络资源加载错误
console.log(error)
}
}, true)
跨域脚本错误
对于不同域的 JavaScript 文件,window.onerror 不能保证获取有效信息。由于安全原因,不同浏览器返回的错误信息参数可能并不一致。比如,跨域之后 window.onerror 在很多浏览器中是无法捕获异常信息的,要统一返回 Script error,这就需要 script 脚本设置为:
<script crossorigin="anonymous"></script>
同时服务器添加 Access-Control-Allow-Origin 以指定允许哪些域的请求访问。
网络资源
script 标签,link 标签进行脚本或者其他资源加载时,由于某种原因(可能是服务器错误,也可能是网络不稳定),导致了脚本请求失败,网络加载错误。
<script src="xxx.js"></script>
<link rel="stylesheet" href="xxx.css">
为了捕获这些加载异常,我们可以:
<script src="xxx.js" onerror="errorHandler(this)"></script>
<link rel="stylesheet" href="xxx.css" onerror="errorHandler(this)">