关于前端异常的处理

923 阅读4分钟

大家有没有遇到过这种情况,项目在测试的时候,测试人员经常过丢给前端一张白屏的页面或者错误UI的截图。但是当你去问测试人员怎么复现的时候,嘿,还复现不了。你说这种bug是修呢,还是不修呢?

前端可以说是最贴近用户的,产品在不断的迭代完善,用户体验也在不断提升。当页面发生异常的时候,相对于页面白屏或者崩溃,在适当的时候,用一种合理的方式去告诉用户发生了什么,可能比让用户炸毛更友好。

总的来说,我们还是要去处理异常,理由如下:

  • 提升用户体验
  • 远程定位问题
  • 未雨绸缪,及早发现问题
  • 不是必现的问题,比如说移动端等

什么是异常

异常算是一种“数据结构”, 它保存了异常发生的相关信息,比如错误码,错误信息等。

ECMA-262规范定义了7种错误类型:

  • Error
  • EvalError
  • RangeError
  • ReferenceError
  • SyntaxError
  • TypeError
  • URIError

Error

Error是所有错误的基类,其他错误都继承自该类型

EvalError

EvalError对象表示全局函数eval()中发生的错误。如果eval()中没有错误,则不会抛出该错误。

RangeError

RangeError对象表示当一个值不在允许值的集合或范围内时出现错误。

ReferenceError

当引用不存在的变量时,该对象表示错误。

SyntaxError

当JavaScript引擎在解析代码时遇到不合法的标记或者标记顺序时,将引发该异常。

TypeError

传递给函数的操作或实参与该操作符或函数的期望类型不兼容时,将引发该异常。

URIError

当全局URI处理函数以错误的方式使用时,将引发该异常。

聊完基础知识,咱们就来具体情况具体分析了。

一般来说,前端有几种异常需要处理:

  • 语法错误
  • 事件异常
  • Ajax请求异常
  • 静态资源加载异常
  • Promise 异常
  • Iframe 异常
  • 页面崩溃

怎么处理

try-catch

使用try-catch只能捕捉到同步的运行时错误,但是对语法和异步错误却无能为力。

Promise.catch()

Promise.catch()可以帮助捕获到异步错误,没有写catch的Promise中抛出的错误无法被onerror或者try-catch捕获,所以务必要在Promise中不要忘记写catch处理抛出的异常。

当然,为了防止有漏掉的Promise异常,建议在全局增加一个对unhandledrejection的舰艇,用来全局监听Uncaught Promise Error。

window.addEventListener('unhandledrejection', function(e)) {
    //add error handle logic
    console.log(e);
}

这样的话,即使忘记对Promise的异常catch,我们也还是可以捕捉到这个异常。

如果想去掉控制台的异常显示,可以加上event.preventDefault()

window.onerror

当JavaScript运行时发生了错误,window会触发一个ErrorEvent接口的error事件,并且执行window.onerror()。

不过不要指望onerror() 万能,它其实对静态资源异常,语法错误或者接口异常的错误都没办法捕捉到。不过我们还是可以用window.onerror()来捕获意料之外的错误。

而且如果不想让onerror函数捕捉到之后不再向上抛出,需要返回true。

window.onerror = function(message, source, lineno, colno, error) {
    console.log('捕捉到异常:', {message, source, lineno, colno, error})
}

PS: onerror最好写在所有JS脚本的前面,否则有可能捕获不到错误。

window.addEventListener

当一项资源加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过能被单一的window.addEventListener捕获。 所以我们可以利用addEventListener来catch那些静态资源加载的异常。

window.frames[0].onerror

window.frames[0].onerror = function (message, source, lineno, colno, error) { 
    console.log('捕获到 iframe 异常:',{message, source, lineno, colno, error}); 
    return true; 
};

不过在项目中没有具体实践过。

Vue.config.errorHandler

Vue.config.errorHandler = function(error, vm, info) {
    //error handle logic here
}

Ajax 请求异常

以axios为例,添加响应拦截器

axios.interceptors.response.use(function (response)) {
        return response
    }, function (error) {
        return Promise.reject(error)
    }
}

总结

  • 可疑区增加try-catch
  • 意外之外的异常 window.onerror
  • 静态资源异常 window.addEventListener
  • 异步异常 Promise.catch
  • 没有catch的Promise unhandlerejection
  • Vue error handler

资料参考: 浅析前端异常及降级处理