前端异常处理

169 阅读3分钟

出错代码对后续代码运行的影响

先看其中的一个例子
示例

运行期,一先一后的两个代码中,出错的一方代码是如何影响另外一方代码继续执行的问题中,跟异步代码没关系,只跟同步代码有关系;跟代码执行期没关系,只跟代码书写期有关系。换句话说就是,异步代码出错与否都不会影响其他代码继续执行。而出错的同步代码,如果它在代码书写期是写在其他代码之前,并且我们并没有对它进行手动地去异常捕获的话,那么它就会影响其他代码(不论它是同步还是异步代码)的继续执行。

如果我们想要保证某块可能出错的同步代码后面的代码继续执行的话,那么我们必须对这块同步代码进行异常捕获

异常上报方式

监控拿到报错信息之后,接下来就需要将捕捉到的错误信息发送到信息收集平台上,常用的发送形式主要有两种:

  • 通过 Ajax 发送数据
  • 动态创建 img 标签的形式 实例 - 动态创建 img 标签进行上报
function error(msg,url,line){
   var REPORT_URL = "xxxx/cgi"; // 收集上报数据的信息
   var m =[msg, url, line, navigator.userAgent, +new Date];// 收集错误信息,发生错误的脚本文件网络地址,用户代理信息,时间
   var url = REPORT_URL + m.join('||');// 组装错误上报信息内容URL
   var img = new Image;
   img.onload = img.onerror = function(){
      img = null;
   };
   img.src = url;// 发送数据到后台cgi
}
// 监听错误上报
window.onerror = function(msg,url,line){
   error(msg,url,line);
}

如何处理异常

  • 可疑区域增加 Try-Catch
  • 全局监控 JS 异常 window.onerror
  • 全局监控静态资源异常 window.addEventListener
  • 捕获没有 CatchPromise 异常:unhandledrejection
  • VUE errorHandlerReact componentDidCatch
  • 监控网页崩溃:window 对象的 loadbeforeunload
  • 跨域 crossOrigin 解决

压缩js的错误定位

通过控制script标签的crossorigin="anonymous"可以捕捉到不同域的js错误信息,在线上的代码都是经过压缩的,可以捕捉到的错误为压缩后的行数和变量,可以通过node提供的source-map模块来定位上报错误信息对应源文件错误的行号

const path = require('path')
const sourceMap = require('source-map')
const fs = require('fs')

const readFile = function (url) {
    return new Promise((resolve, reject) => {
        fs.readFile(url, (err, res) => {
            if (err) {
                reject(err)
            } else {
                resolve(res);
            }
        })
    })
}

// 客户端传过来生产环境下的js文件
const error = {"message":"Uncaught ReferenceError: a is not defined","source":"http://localhost:3000/dist/index.min.js","line":1,"column":588,"error":"ReferenceError: a is not defined"}

console.log(error);

// 根据source获取source map文件
async function getSourceMap(source) {
    let basename = path.basename(source);
    let sm = await readFile(path.join(__dirname, './dist/' + basename + '.map'));
    let smObj = {};
    try {
        smObj = JSON.parse(sm);
    } catch (err) {
        console.log('找不到对应的source map文件')
    }
    return smObj;
}

async function analyze(errObj) {
    let rawSourceMap = await getSourceMap(errObj.source);
    try {
        await sourceMap.SourceMapConsumer.with(rawSourceMap, null, consumer => {
            let sourcePos = consumer.originalPositionFor({
                line: errObj.line,
                column: errObj.column
            });
            Object.assign(errObj, sourcePos);
            return errObj;
        });
    } catch (err) {
        console.log(err.message);
    }
    return errObj;
}

analyze(error).then(res => {
	// 定位错误后的具体信息
    console.log(res);
});

这种方法待验证

开源项目:推荐使用Sentry做日志监控