js异常处理

为何要处理异常?

提升用户体验,及早发现/定位问题(比如机型,系统,移动端某些无法复现的问题)

常见异常种类

  1. js 语法错误
  2. ajax 请求报错
  3. 静态资源加载异常
  4. promise 异常
  5. iframe 异常
  6. 跨域 Script Error
  7. 崩溃/卡顿

异常处理

  1. try...catch:
  • 只能捕捉到同步的运行时错误
// 同步错误 ok
try {
  let name = 'name';
  console.log(nam);
} catch (err) {
  console.log(err);
}
  • 不能捕捉到语法错误,不能捕捉到异步错误
try {
    // 语法错误 not ok
  let name = 'jartto;
  console.log(nam);

// 异步错误 not ok
//    setTimeout(() => {
//     undefined.map(v => v);
//   }, 1000)
} catch(e) {

  console.log('捕获到异常:',e);
}
  1. window.onerror:当 JS 运行时错误发生时,window 会触发一个 ErrorEvent 接口的 error 事件,并执行 window.onerror()。
    • 可捕捉:同步错误,异步错误
    • 不可捕捉:语法错误,静态资源错误,接口异常
      注意:
      1. window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx
      2. 必须放在所有脚本前面才能捕获错误
// @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 });

  return true;
};
// 同步 ok
throw new Error('error');

// 异步 ok
setTimeout(() => {
  throw new Error('error');
});

// 语法错误 not ok
let name = 'name

// 网络异常 not ok
let img = new Image()
img.src = './img.png'
  1. window.addEventListener
    当一项资源(如图片或脚本)加载失败,加载资源的元素会触发一个 Event 接口的 error 事件,并执行该元素上的 onerror() 处理函数。这些 error 事件不会向上冒泡到 window ,不过(至少在 Firefox 中)能被单一的 window.addEventListener 捕获。
    注意:

    1. 网络请求不会冒泡,所以需要在捕获阶段捕捉到,但是无法判断 HTTP 状态码,需要配合服务器日志排查
    2. 避免重复监听/注意不同浏览器的兼容处理
    window.addEventListener(
      'error',
      error => {
        console.log('捕获到异常:', error);
      },
      true,
    );
    let img = new Image();
    img.src = './img.png';
    img.onload = function (e) {
      console.log(e);
    };
    img.onerror = function (e) {
      console.log(e);
    };
    document.body.appendChild(img);
    
  2. Promise Catch
    在 promise 中可以用 catch 捕捉错误,没有被 catch 的错误也无法被 onerror 或 try-catch 捕获到,为了防止部分 promise 错误被漏掉,在全局增加一个 unhandledrejecttion 处理

    window.addEventListener('unhandledrejection', function (e) {
      e.preventDefault(); // 去掉控制台错误显示
      console.log('捕获到异常:', e);
      return true;
    });
    Promise.reject('promise error');
    
  3. Vue errorHandler
    捕捉计算属性/方法运行时错误

Vue.config.errorHandler = (err, vm, info) => {
  console.error('通过vue errorHandler捕获的错误');
  console.error(err);
  console.error(vm);
  console.error(info);
};
  1. React componentDidCatch
componentDidCatch(error, info) {
    console.log(error, info);
}
  1. iframe 异常 借助 window.onerror


  1. script error
//  跨源资源共享机制( CORS ):我们为 script 标签添加 crossOrigin 属性。
;

// 动态添加脚本
const script = document.createElement('script');
script.crossOrigin = 'anonymous';
script.src = url;
document.body.appendChild(script);
(() => {
   const originAddEventListener = EventTarget.prototype.addEventListener;
   EventTarget.prototype.addEventListener = function (type, listener, options) {
    // 捕获添加事件时的堆栈
     const addStack = newError(`Event (${type})`).stack;
      const wrappedListener = function (...args) {
       try {
         return listener.apply(this, args);
       }
       catch (err) {
        // 异常发生时,扩展堆栈
         err.stack += '\n' + addStack;
         throw err;
       }
     }
     return originAddEventListener.call(this, type, wrappedListener, options);
   }
 })();
  1. 崩溃和卡顿: window 的 load 和 beforeunload 或者 service worker
    1. 在网页加载后,不断更新 session 中的时间,在登出后,将登出态设置为正常登出
    2. 判断上次是否是正常登出,获取最后一次时间
// 使用定时器
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. 错误上报
    1. ajax 发送数据,但是 ajax 也可能发生异常
    2. 动态创建 img
function report(error) {
  let reportUrl = 'http://jartto.wang/report';
  new Image().src = `${reportUrl}?logs=${error}`;
}
3. 优化: 过多的错误可能导致崩溃, 采集率
Reporter.send = function (data) {
  // 只采集 30%
  if (Math.random() < 0.3) {
    send(data); // 上报错误信息
  }
};

总结

异常处理

  1. try...catch: 可疑区域监控(同步错误)
  2. window.onerror : 全局 js 监控异常(同步/异步错误)
  3. window.addEventListener: 全局监控静态资源异常(网络请求/同步/异步错误)
  4. unhandledrejection: 捕获未 catch 的异常
  5. VUE errorHandler 和 React componentDidCatch
  6. window.load 和 window.beforeunload :监控网页崩溃
  7. 跨域:crossOrigin

参考:https://mp.weixin.qq.com/s/prf-mXexBh1Ie-ctq9FnzA
参考:http://jartto.wang/2018/11/20/js-exception-handling/

你可能感兴趣的:(js异常处理)