目录
前言
错误定位
补充
错误分析/错误定位(服务端)
前言
在现代Web应用开发中,用户体验的优化与系统稳定性的保障成为了开发者关注的重点。前端作为用户直接交互的界面层,其性能与稳定性直接影响到用户的使用体验。然而,在实际的开发过程中,由于各种不可预见的因素,如代码逻辑错误、网络问题、浏览器兼容性等,前端应用时常会遇到各种异常情况。这些问题不仅影响了用户体验,也给开发团队带来了不小的挑战。
为了有效提升应用的质量与稳定性,前端错误捕获与定位技术应运而生。通过合理运用这些技术,我们可以及时发现并修复问题,甚至在用户察觉之前就将潜在的问题解决,从而提供更加流畅和可靠的用户体验。本文将深入探讨几种常用的前端错误捕获方法,以及如何利用现有工具和技术进行高效的错误定位。无论你是刚入门的新手还是经验丰富的资深开发者,相信都能从本文中获得有价值的见解和实用的技巧。
错误定位
前端的错误捕获我们最常见的当然是window.onerror
了,我们可以通过定义window.onerror
函数来对全局错误进行捕获。
window.onerror = function(message, source, lineno, colno) {
console.log(message)
console.log(source)
console.log(lineno)
console.log(colno)
}
但window.onerror
并不能捕获到框架组件生命周期的错误,所以我们可以再补充一个框架的错误捕获,以Vue为例:
const app = createApp(App)
app.use(store).use(router).mount('#app')
app.config.errorHandler = function (err, vm, info) {
console.log(err)
console.log(vm)
console.log(info)
};
我们在errorHandler事件中,可以拿到错误对象err,vue实例,错误信息。这里我们并不能像上面onerror错误捕获一样很方便的取出出错的行数和列数,但我们能够拿到一个完整的错误堆栈对象,那么我们就可以对错误对象的堆栈信息进行处理,提取出我们想要的行数和列数。
这里用到了一个堆栈解析工具——StackTrace-Parser
npm install stacktrace-parser
app.config.errorHandler = function (err, vm, info) {
const errInfo = stackTraceParser.parse(err.stack)[0]
const message = err.message // 错误message
const lineno = errInfo.lineNumber // 错误行数
const colno = errInfo.column // 错误列数
const source = errInfo.file // 错误出现的文件名
...
};
补充
错误捕获还有一个onunhandledrejection的事件,用于捕获Promise类型的错误,但是经过尝试发现不是很好去拿到错误的定位信息,同时,考虑到一般Promise我们会使用catch
去处理异常的操作,所以这里就暂时不处理这个类型的错误事件了。
至此,我们的捕获相关的逻辑已经完成,剩下的就是如何设计服务端,如何将这些信息传递给服务端并完成解析了。
错误分析/错误定位(服务端)
服务端,我们设计两个接口,一个用于上传map文件(upload),一个用于接收错误信息(sendErrorLog)。
上传接口就不多说了,主要就是在前端打包完成之后,服务端接收传过来的map文件。我们主要看一下接收错误信息的接口逻辑。
const handleErrorMessage = require("./utils/index");
app.post("/sendErrorLog", (req, res) => {
handleErrorMessage(req.body);
res.send("hello");
});
const fs = require("fs");
const { SourceMapConsumer } = require("source-map");
const path = require("path");
// 读取压缩代码和对应的source map
const arr = fs.readdirSync(path.resolve(__dirname, "../uploads"));
const sourceMap = {};
for (let i = 0; i < arr.length; i++) {
fs.readFile(
path.resolve(__dirname, "../uploads", arr[i]),
"utf-8",
function (err, data) {
if (err) {
return err;
}
sourceMap[arr[i]] = data;
}
);
}
module.exports = function handleErrorMessage(message) {
const errorLine = message.lineno;
const errorCol = message.colno;
const jsName = message.source.split("/").pop();
const sourceName = jsName + ".map";
// 服务器因为是一直启动状态,所以如果是在启动后最新上传的文件,则需要事实进行读取对应的map文件
if (!sourceMap[sourceName]) {
sourceMap[sourceName] = fs.readFileSync(
path.resolve(__dirname, "../uploads", sourceName),
"utf-8"
);
}
SourceMapConsumer.with(sourceMap[sourceName], null, (consumer) => {
// 在源码堆栈中定位报错位置
const originalPosition = consumer.originalPositionFor({
line: errorLine,
column: errorCol,
});
console.log("Error occurred at:");
console.log("file:" + originalPosition.source);
console.log("line:" + originalPosition.line);
console.log("column:" + originalPosition.column);
console.log("message:" + message.message);
});
};