如何优雅处理前端异常?(史上最全前端异常处理方案)
为什么要处理异常
异常是不可控的,会影响最终的呈现结果,但是我们有充分的理由去做这样的事情。
- 增强用户体验
- 远程定位问题
- 未雨绸缪,及早发现问题
- 无法复线问题,尤其是移动端 机型 系统都是问题
- 完善的前端方案,前端监控系统
对于JS而言,我们面对的仅仅只是异常,异常的出现不会直接导致JS引擎崩溃,最多只会使当前执行的任务终止。
需要处理哪些异常
- JS语法错误 代码异常
- AJAX请求异常
- 静态资源异常
- Promise异常
- Iframe异常
- 跨域 Script error
- 奔溃和卡顿
Try-Catch的误区
try-catch只能捕获到同步的运行时错误,对语法和异步错误却无能为力,捕获不到。
- 同步运行时错误
- 不能捕获到具体的语法错误,只有一个语法错误提示。
- 不能捕获到异步错误。
window.onerror 不是万能的
当JS运行时错误发生时,window 会触发一个 ErrorEvent接口的error事件,并执行 window.onerror()。 ``` /**
- @param {String} message 错误信息
- @param {String} source 出错文件
- @param {Number} lineno 行号
- @param {Number} colno 列号
- @param {Object} error Error对象(对象) */
window.onerror = function(message, source, lineno, colno, error) { console.log(‘捕获到异常:’,{message, source, lineno, colno, error}); }
运行时错误 可以捕获<br />语法错误 不能捕获<br />异步错误可以捕获<br />静态资源异常 或者接口异常 错误都无法捕获到。<br />window.onerror 函数只有返回 true的时候,异常才不会向上抛出,否则即使时知道异常的发生控制台还是会显示。<br />需要注意
- onerror 最好写在所有JS脚本的前面,否则有可能捕获不到错误
- onerror无法捕获语法错误
在实际的使用过程中,onerror 主要是用来捕获意料之外的错误,而ty-catch则是用来在可预见情况下监控特定的错误,两者结合使用更好
<a name="Z1cgb"></a>
#### window.addEventListener
当一项资源(如图片或脚本)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的 onerror()处理函数。这些error事件不会向上冒泡到window,不过(至少在Firefox)能被单一的 window.addEventListener捕获。
<a name="ug1pg"></a>
#### Promsie Catch
为了防止有漏掉的Promsie异常,建议在全局增加一个对 unhandledrejection的监听,用来全局监听 Uncaught Promsie Error
window.addEventListener(“unhandledrejection”, function(e){ e.preventDefault() console.log(‘捕获到异常:’, e); return true; }); Promise.reject(‘promise error’);
<a name="Monb9"></a>
### Vue errorHandler
Vue.config.errorHandler = (err, vm, info) => { console.error(‘通过vue errorHandler捕获的错误’); console.error(err); console.error(vm); console.error(info); }
``` ### Script error 一般情况,如果出现Script error这样错误,基本上可以确定是出现了跨域问题。
<a name="uhkqU"></a>
### iframe异常
对于 iframe的异常捕获, 还得接力 window.onerror
跨源资源共享机制(CORS) 我们为 script标签添加 crossOrigin 属性 ```
const script = document.createElement(‘script’); script.crossOrigin = ‘anonymous’; script.src = url; document.body.appendChild(script);
特别注意,服务器端需要设置 Access-Controll-Allow-Origin
<a name="aXnSL"></a>
### 崩溃和卡顿
卡顿也就是网页暂时响应比较慢,JS可能无法及时执行。但崩溃就不一样了,网页崩溃了,JS都不运行啦。
1. 利用window对象的 load 和 beforeunload事件实现了网页崩溃的监控。
window.addEventListener(‘load’, function () { sessionStorage.setItem(‘good_exit’, ‘pending’); setInterval(function () { sessionStorage.setItem(‘time_before_crash’, newDate().toString()); }, 1000); });
window.addEventListener(‘beforeunload’, function () { sessionStorage.setItem(‘good_exit’, ‘true’); });
if(sessionStorage.getItem(‘good_exit’) && sessionStorage.getItem(‘good_exit’) !== ‘true’) { / insert crash logging code here / alert(‘Hey, welcome back from your crash, looks like you crashed on: ‘ + sessionStorage.getItem(‘time_before_crash’)); }
2. 基于一下原因,可以使用Service Worker 来实现网页崩溃的监控
Service Worker 有自己单独的工作线程,与网页区分开来,网页崩溃了,Service Worker 一般情况下不会崩溃;<br />Service Worker 生命周期一般要比网页还要长,可以用来监控网页的状态;<br />网页可以通过 navigator.serviceWorker.controller.postMessage API 向掌管自己的 SW 发送消息。
<a name="F4Eag"></a>
### 错误上报
function report(error) {
let reportUrl = ‘http://jartto.wang/report‘;
new Image().src = ${reportUrl}?logs=${error}
;
}
Reporter.send = function(data) {
// 只采集 30%
if(Math.random() < 0.3) {
send(data) // 上报错误信息
}
}
```
总结
- 可疑区域增加 Try-Catch
- 全局监控JS异常 window.onerror
- 全局监控静态资源异常 window.addEventListener
- 捕获没有Catch的Promise异常 unhandledrejection
- vue errorHandler 和React componentDidCatch
- 监控网页崩溃 window对象的 load 和 befoerload Service Worker
- 跨域 crossOrigin 解决