错误信息收集方式| 8月更文挑战

444 阅读4分钟

这是我参与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)">