前言
随着前端领域的快速发展,对于产品性能的要求也随之越来越高。虽然产品需要通过研发自测和专业测试人员测试,但是到了用户使用阶段依然会出现一些bug。对于这一些bug也只有少许用户会进行反馈或者因为这些bug的原因用户选择放弃使用产品,而且这些通过研发自测和专业测试人员测试出现的bug是难以复现的。因此对于bug的及时发现和解决,成为了优化产品的重心。
常见错误
语法错误
语法错误常见于开发阶段,现代前端工程化几乎代码都需要经过编译,所以开发阶段就会发现和解决。(常见语法错误英文字符写成中文字符)
注意:语法错误无法被try catch 处理
同步错误
javascript在执行脚本时,把任务分块压入执行栈中,轮询取出执行,每个任务都有自己的执行上下文环境,在当前执行上下文环境同步执行的代码发生错误都能被try catch 捕获,保证后续的同步代码被执行。
异步错误
常见的setTimeout、setInterval等方法因为javascript事件循环的机制,会在上一个宏任务(macrotasks)执行完毕后执行,所以try catch无法捕获其他上下文的代码错误。
为了便于分析发生的错误,一般利用 window.onerror 事件来监听错误的发生。 它比try catch的捕获错误信息的能力要强大。
/**
* @param {String} msg 错误描述
* @param {String} url 报错文件
* @param {Number} row 行号
* @param {Number} col 列号
* @param {Object} error 错误Error对象
*/
window.onerror = function (msg: string, url: string, row: number, col: number, error: Error) {
console.log('我知道错误了');
// return true; // 返回 true 的时候,异常不会向上抛出,控制台不会输出错误
};
- window.onerror 注意事项
- window.onerror 可以捕获常见语法、同步、异步错误等错误;
- window.onerror 无法捕获 Promise 错误、网络错误;
- window.onerror 应该在所有JS脚本之前被执行,以免遗漏;
- window.onerror 容易被覆盖,在处理回调时应该考虑,被人也在使用该事件监听。
网络错误
由于网络请求异常不会冒泡,应此需要在事件捕获阶段才能获取到。我们可以利用 window.addEventListener。比如代码、图片等重要 CDN 资源挂了,能及时获得反馈是极为重要的。
window.addEventListener('error', (e: Event) => {
// 避免重复上报
if (target !== window) {
console.log(e);
}
// return true; // 中断事件传播
}, true);
Promise 错误
如果你在使用 promise 时未 catch 的话,那么 onerror 也无能为力了。
Promise.reject('promise error');
new Promise((resolve, reject) => {
reject('promise error');
});
new Promise((resolve) => {
resolve();
}).then(() => {
throw 'promise error';
});
window.addEventListener("unhandledrejection")来监控错误。 接收一个PromiseError对象,可以解析错误对象中的 reason 属性,有点类似 stack。 具体兼容处理在 (TraceKit.js)[github.com/csnover/Tra…] 可以看到。
实战
前端代码
import TraceKit from 'tracekit';
import {createWebPageError, createWebPageResourceError} from "../../../service/monitor";
export default class ErrorMonitor {
private static instance: ErrorMonitor;
static getInstance(): ErrorMonitor {
// 如果 instance 是一个实例 直接返回, 如果不是 实例化后返回
if (!ErrorMonitor.instance) {
ErrorMonitor.instance = new ErrorMonitor()
}
return ErrorMonitor.instance
}
constructor() {
ErrorMonitor.init();
}
/**
* 常见语法、同步、异步错误等错误回调
* @param message
* @param url
* @param lineNum
* @param columnNum
* @param error
*/
protected static windowErrorHandle(message: string, url: string, lineNum: number, columnNum: number, error: Error): boolean {
/* eslint-disable */
const {location: {href}} = window;
createWebPageError({href, message, url, lineNum, columnNum, error: JSON.stringify(error, ['message', 'stack'])});
/* eslint-disable */
return true;
};
/**
* 资源加载错误回调
* @param e
*/
protected static networkErrorHandle(e: Event) {
const target = e.target || e.srcElement;
if (target !== window) {
const {location: {href}} = window;
const {src} = target as any;
createWebPageResourceError({href, src});
}
}
/**
* Promise错误回调
* @param e
*/
protected static promiseErrorHandle(e: ErrorEvent) {
e.preventDefault();
TraceKit.report((e as any).reason);
return true;
}
protected static bindEvent() {
window.onerror = ErrorMonitor.windowErrorHandle;
window.addEventListener('error', ErrorMonitor.networkErrorHandle, true);
window.addEventListener('unhandledrejection', ErrorMonitor.promiseErrorHandle);
}
protected static init() {
ErrorMonitor.bindEvent();
};
}
后端数据库
CREATE TABLE IF NOT EXISTS `page_error` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userId` int(11) DEFAULT NULL COMMENT '用户id',
`equipmentId` int(11) DEFAULT NULL COMMENT '设备id',
`href` varchar(255) DEFAULT NULL COMMENT '报错页面地址',
`message` varchar(255) DEFAULT NULL COMMENT '错误描述',
`url` varchar(255) DEFAULT NULL COMMENT '报错文件',
`lineNum` int(11) DEFAULT NULL COMMENT '报错行号',
`columnNum` int(11) DEFAULT NULL COMMENT '报错列号',
`error` longtext DEFAULT NULL COMMENT '错误Error对象',
`createTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
最后
前端错误监控前端开发人员必备的知识,通过接收错误数据及时解决线上问题,提升用户体验。
个人技术博客已经使用typescript + react hooks + antd 重构完成,有兴趣学习的小伙伴可以看看!