JS异常
方案 | 描述 |
---|---|
window.onerror | developer.mozilla.org/zh-CN/docs/… |
window.addEventListener('error') | developer.mozilla.org/en-US/docs/… |
window.onerror
不能捕获静态资源报错,因为当一项资源(如<img>或<script>)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window。
错误类型 | 描述 |
---|---|
EvalError | 与eval()相关的错误。eval()本身没有正确执行 |
RangeError | 数值变量或参数超出其有效范围。例子:var a = new Array(-1); |
ReferenceError | 语法错误。例子:var a = ; |
TypeError | 变量或参数不属于有效范围。例子:[1,2].split('.') |
URIError | 给 encodeURI或 decodeURl()传递的参数无效。例子:decodeURI('%2') |
AggregateError | 当多个错误需要包装在一个错误中时,该对象表示一个错误;当需要由操作报告多个错误被抛出,例如通过Promise.any(),在传递给它的所有Promise拒绝。 |
InternalError | 创建一个代表Javascript引擎内部错误的异常抛出的实例。 如: "递归太多"。非ECMAScript标准。 |
DOMException | 接口代表调用方法或访问 Web API 属性时发生的异常事件(被称为异常,exception) |
RangeError
window.onerror = function(message, source, lineno, colno, error) {
console.log('错误信息(字符串):',message);
console.log('发生错误的脚本URL:',source);
console.log('发生错误的行号:',lineno);
console.log('发生错误的列号:',colno);
console.log('Error对象:',error);
}
var a = new Array(-1);
DOMException
window.onerror = function(message, source, lineno, colno, error) {
console.log('错误信息(字符串):',message);
console.log('发生错误的脚本URL:',source);
console.log('发生错误的行号:',lineno);
console.log('发生错误的列号:',colno);
console.log('Error对象:',error);
}
var node = document.querySelector('#app');
var refnode = node.nextSibling;
var newnode = document.createElement('div');
node.insertBefore(newnode, refnode);
Promise异常
promise抛出异常,window.onerror捕获不到,需要用到window.addEventListener('unhandledrejection')
window.addEventListener('unhandledrejection',function(promiseRejectionEvent){
console.log(promiseRejectionEvent.reason)
})
const p1 = new Promise(function(resolve,reject){
resolve(a+b);
});
p1.then(function(value){
console.log(value)
})
console.error
在某种场景下,也需要捕获业务系统的console.error异常
window.console.error = function(){
console.log('捕获到了console.error异常,参数是:',arguments);
consoleError && consoleError.apply(window,arguments);
}
console.error('这是一个console.error异常')
框架异常
Vue异常捕获
app.config.errorHandler = function(err, vm, info) {
console.log(err, vm, info, "---a00-----");
};
静态资源异常
当一项资源(如<img>或<script>)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过能被window.addEventListener在捕获阶段捕获。所以useCapture要传值为true,才能捕获到静态资源异常.
window.addEventListener('error',function(event){
console.log(event,'event');
},true)
跨域脚本error
当加载自不同域的脚本中发生语法错误时,为避免信息泄露(参见bug 363897),语法错误的细节将不会报告,而代之简单的"Script error."。
在某些浏览器中,通过在<script>使用crossorigin属性并要求服务器发送适当的 CORS HTTP 响应头,该行为可被覆盖。一个变通方案是单独处理"Script error.",告知错误详情仅能通过浏览器控制台查看。
接口异常
接口异常后端可以进行接口拦截,后端进行处理,这里前端就不列举方案;
需要解决问题
现在的前端项目,如果一旦发布上线,代码一般都会进行混淆、压缩甚至加密,线上报错时,定位到的行号始终为1,如下面例子,这样,我们监控到的错误数据就没有意义,需要解决这个问题,理想的结果是能够精确定位到某个文件的多少行,多少列
解决办法
引入source-map包
var fs = require('fs'),
path = require('path'),
sourceMap = require('source-map');
// 要解析的map文件路径./test/vendor.8b1e40e47e1cc4a3533b.js.map
var GENERATED_FILE = path.join(
'.',
'dist/js',
'app.5025c1aa.js.map'
)
let getSource = async()=>{
// 读取map文件,实际就是一个json文件
var rawSourceMap = fs.readFileSync(GENERATED_FILE).toString();
// 通过sourceMap库转换为sourceMapConsumer对象
var consumer = await new sourceMap.SourceMapConsumer(rawSourceMap);
// 传入要查找的行列数,查找到压缩前的源文件及行列数
var sm = consumer.originalPositionFor({
line: 1, // 压缩后的行数
column: 224607 // 压缩后的列数
});
console.log('源文件行列数:',sm)
// 压缩前的所有源文件列表
var sources = consumer.sources;
// 根据查到的source,到源文件列表中查找索引位置
var smIndex = sources.indexOf(sm.source);
// 到源码列表中查到源代码
var smContent = consumer.sourcesContent[smIndex];
// 将源代码串按"行结束标记"拆分为数组形式
const rawLines = smContent.split(/\r?\n/g);
// 输出源码行,因为数组索引从0开始,故行数需要-1
console.log(rawLines.join('\n'))
}
getSource();
根据source-map文件,可以定位到具体的某个文件的某一行,某一列报错。该问题已经被解决。