如何优雅处理前端异常?(史上最全前端异常处理方案)

为什么要处理异常

异常是不可控的,会影响最终的呈现结果,但是我们有充分的理由去做这样的事情。

  1. 增强用户体验
  2. 远程定位问题
  3. 未雨绸缪,及早发现问题
  4. 无法复线问题,尤其是移动端 机型 系统都是问题
  5. 完善的前端方案,前端监控系统

对于JS而言,我们面对的仅仅只是异常,异常的出现不会直接导致JS引擎崩溃,最多只会使当前执行的任务终止。

需要处理哪些异常

  1. JS语法错误 代码异常
  2. AJAX请求异常
  3. 静态资源异常
  4. Promise异常
  5. Iframe异常
  6. 跨域 Script error
  7. 奔溃和卡顿

Try-Catch的误区

try-catch只能捕获到同步的运行时错误,对语法和异步错误却无能为力,捕获不到。

  1. 同步运行时错误
  2. 不能捕获到具体的语法错误,只有一个语法错误提示。
  3. 不能捕获到异步错误。

    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}); }

  1. 运行时错误 可以捕获<br />语法错误 不能捕获<br />异步错误可以捕获<br />静态资源异常 或者接口异常 错误都无法捕获到。<br />window.onerror 函数只有返回 true的时候,异常才不会向上抛出,否则即使时知道异常的发生控制台还是会显示。<br />需要注意
  2. - onerror 最好写在所有JS脚本的前面,否则有可能捕获不到错误
  3. - onerror无法捕获语法错误
  4. 在实际的使用过程中,onerror 主要是用来捕获意料之外的错误,而ty-catch则是用来在可预见情况下监控特定的错误,两者结合使用更好
  5. <a name="Z1cgb"></a>
  6. #### window.addEventListener
  7. 当一项资源(如图片或脚本)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的 onerror()处理函数。这些error事件不会向上冒泡到window,不过(至少在Firefox)能被单一的 window.addEventListener捕获。

window.addEventListener(‘error’, (error) => { console.log(‘捕获到异常:’, error); }, true) 如何优雅处理前端异常 - 图1

  1. <a name="ug1pg"></a>
  2. #### Promsie Catch
  3. 为了防止有漏掉的Promsie异常,建议在全局增加一个对 unhandledrejection的监听,用来全局监听 Uncaught Promsie Error

window.addEventListener(“unhandledrejection”, function(e){ e.preventDefault() console.log(‘捕获到异常:’, e); return true; }); Promise.reject(‘promise error’);

  1. <a name="Monb9"></a>
  2. ### Vue errorHandler

Vue.config.errorHandler = (err, vm, info) => { console.error(‘通过vue errorHandler捕获的错误’); console.error(err); console.error(vm); console.error(info); }

  1. <a name="uhkqU"></a>
  2. ### iframe异常
  3. 对于 iframe的异常捕获, 还得接力 window.onerror

``` ### Script error 一般情况,如果出现Script error这样错误,基本上可以确定是出现了跨域问题。
跨源资源共享机制(CORS) 我们为 script标签添加 crossOrigin 属性 ```

const script = document.createElement(‘script’); script.crossOrigin = ‘anonymous’; script.src = url; document.body.appendChild(script);

  1. 特别注意,服务器端需要设置 Access-Controll-Allow-Origin
  2. <a name="aXnSL"></a>
  3. ### 崩溃和卡顿
  4. 卡顿也就是网页暂时响应比较慢,JS可能无法及时执行。但崩溃就不一样了,网页崩溃了,JS都不运行啦。
  5. 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’)); }

  1. 2. 基于一下原因,可以使用Service Worker 来实现网页崩溃的监控
  2. Service Worker 有自己单独的工作线程,与网页区分开来,网页崩溃了,Service Worker 一般情况下不会崩溃;<br />Service Worker 生命周期一般要比网页还要长,可以用来监控网页的状态;<br />网页可以通过 navigator.serviceWorker.controller.postMessage API 向掌管自己的 SW 发送消息。
  3. <a name="F4Eag"></a>
  4. ### 错误上报

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) // 上报错误信息 } } ```

总结

  1. 可疑区域增加 Try-Catch
  2. 全局监控JS异常 window.onerror
  3. 全局监控静态资源异常 window.addEventListener
  4. 捕获没有Catch的Promise异常 unhandledrejection
  5. vue errorHandler 和React componentDidCatch
  6. 监控网页崩溃 window对象的 load 和 befoerload Service Worker
  7. 跨域 crossOrigin 解决