在讲错误收集方式之前,先理清一下前端编码时有那些常见错误
前端错误收集是保障用户体验、排查线上问题的关键手段。本文将从多个角度详解前端错误收集的各种方式,包括不同框架(如 React、Vue)、JavaScript 执行环境中的同步/异步/Promise 错误、资源加载错误(如图片、script 加载失败)等。以下是全面的错误捕获机制总结:
🌐 一、通用错误收集方式(原生 JavaScript)
1. try...catch
捕获同步代码错误
1try {
2
3 let result = a.b;
4} catch (err) {
5 console.error('捕获同步错误:', err);
6}
注意:
- 只能捕获同步代码错误,不能捕获异步或事件回调内的错误。
2. window.onerror
1window.onerror = function (message, source, lineno, colno, error) {
2 console.error('window.onerror 捕获:', { message, source, lineno, colno, error });
3};
特性:
- 可捕获运行时错误,包括未捕获的异常。
- 跨域脚本错误默认不会暴露具体信息,需设置
crossorigin
属性和响应头。
3. window.addEventListener('error')
捕获资源加载错误
1window.addEventListener('error', function (event) {
2 if (event.target instanceof HTMLScriptElement || event.target instanceof HTMLImageElement) {
3 console.error('资源加载错误:', event.target.src || event.target.href);
4 } else {
5 console.error('运行时错误:', event.message);
6 }
7}, true);
优点:
- 能捕获
<img>
,<script>
,<link>
等静态资源加载错误。
4. unhandledrejection
捕获未处理的 Promise 异常
1window.addEventListener('unhandledrejection', function (event) {
2 console.error('未处理的 Promise 异常:', event.reason);
3});
场景:
- Promise 的
.catch()
未添加或写在不合适位置。
⚛️ 二、React 中的错误捕获
1. React 16+ 错误边界(Error Boundaries)
1class ErrorBoundary extends React.Component {
2 state = { hasError: false, error: null };
3
4 static getDerivedStateFromError(error) {
5 return { hasError: true };
6 }
7
8 componentDidCatch(error, info) {
9 console.error('React 错误捕获:', error, info);
10 }
11
12 render() {
13 if (this.state.hasError) {
14 return <h2>出错了</h2>;
15 }
16 return this.props.children;
17 }
18}
限制:
-
只能捕获子组件生命周期、render、构造函数中的错误。
-
不能捕获:
- 异步代码(如
setTimeout
) - 事件处理函数错误
- SSR 期间的错误
- 异步代码(如
🐸 三、Vue 中的错误捕获
1. Vue 2.x errorHandler
1Vue.config.errorHandler = function (err, vm, info) {
2 console.error('Vue 错误:', err, info);
3};
2. Vue 3.x app.config.errorHandler
1const app = Vue.createApp(App);
2
3app.config.errorHandler = (err, instance, info) => {
4 console.error('Vue3 错误:', err, info);
5};
3. 异步函数中的错误
异步函数内的错误需要使用 try/catch 或监听 unhandledrejection
。
🧠 四、异步任务中的错误处理方式
1. setTimeout
、setInterval
1setTimeout(() => {
2 try {
3 throw new Error('异步定时错误');
4 } catch (err) {
5 console.error('捕获异步错误:', err);
6 }
7}, 1000);
2. Promise
中的异常
1new Promise((resolve, reject) => {
2 throw new Error('Promise 内部异常');
3});
3. async/await
异常处理
1async function test() {
2 try {
3 await fetch('...');
4 throw new Error('异步错误');
5 } catch (e) {
6 console.error('async/await 错误:', e);
7 }
8}
🖼️ 五、资源加载错误(图片、脚本等)
图片加载失败
1<img src="wrong.png" onerror="console.error('图片加载失败')" />
脚本加载失败
1<script src="wrong.js" onerror="console.error('JS 加载失败')"></script>
全局监听
1window.addEventListener('error', function (e) {
2 if (e.target.tagName === 'IMG' || e.target.tagName === 'SCRIPT') {
3 console.error('资源加载失败:', e.target.src || e.target.href);
4 }
5}, true);
📦 六、前端错误上报实践建议
-
统一封装错误上报工具
1function reportError(errorInfo) { 2 3 fetch('/log', { 4 method: 'POST', 5 body: JSON.stringify(errorInfo), 6 }); 7}
-
错误去重、限频
- 防止相同错误反复上报。
-
结合 Source Map
- 错误行号定位需要 source map 还原。
-
配合监控平台(如 Sentry、Fundebug、LogRocket)
- 可视化查看错误堆栈、用户行为路径等。
✅ 总结
类型 | 捕获方式 | 说明 |
---|---|---|
同步代码异常 | try...catch | 推荐搭配日志上报使用 |
全局 JS 错误 | window.onerror | 支持堆栈、来源信息 |
静态资源加载失败 | window.addEventListener('error', ..., true) | 捕获 <script> /<img> /<link> 加载失败 |
Promise 未处理异常 | window.unhandledrejection | 捕获 Promise 中未被 .catch 处理的异常 |
React | 错误边界组件 | 捕获渲染和生命周期异常 |
Vue | config.errorHandler | 支持全局组件异常处理 |
async/await 异常 | try/catch | 手动捕获为主 |
如需我帮你生成一个完整的错误收集 SDK、结合你项目的架构,欢迎告诉我。
下面是一个通用的前端错误收集 SDK,适用于纯 JavaScript 应用,也兼容 React、Vue 等框架。具备如下能力:
- 捕获同步错误(try/catch)
- 捕获未处理的 Promise 错误
- 捕获资源加载失败(如图片、脚本)
- 可扩展支持自定义上报方式
- 可用于构建自己的埋点/监控平台
✅ 错误收集 SDK 示例(error-tracker.js
)
1class ErrorTracker {
2 constructor(options = {}) {
3 this.reportUrl = options.reportUrl || '/report-error';
4 this.extraContext = options.context || {};
5 this.init();
6 }
7
8 init() {
9 this.bindGlobalError();
10 this.bindPromiseRejection();
11 this.bindResourceError();
12 }
13
14 bindGlobalError() {
15 window.onerror = (message, source, lineno, colno, error) => {
16 const errorInfo = {
17 type: 'js-error',
18 message,
19 source,
20 lineno,
21 colno,
22 stack: error?.stack,
23 ...this.extraContext,
24 };
25 this.report(errorInfo);
26 };
27 }
28
29 bindPromiseRejection() {
30 window.addEventListener('unhandledrejection', (event) => {
31 const errorInfo = {
32 type: 'unhandledrejection',
33 reason: event.reason?.message || String(event.reason),
34 stack: event.reason?.stack || null,
35 ...this.extraContext,
36 };
37 this.report(errorInfo);
38 });
39 }
40
41 bindResourceError() {
42 window.addEventListener(
43 'error',
44 (event) => {
45 if (event.target && (event.target.src || event.target.href)) {
46 const errorInfo = {
47 type: 'resource-error',
48 tagName: event.target.tagName,
49 url: event.target.src || event.target.href,
50 outerHTML: event.target.outerHTML,
51 ...this.extraContext,
52 };
53 this.report(errorInfo);
54 }
55 },
56 true
57 );
58 }
59
60 report(errorData) {
61 try {
62 navigator.sendBeacon
63 ? navigator.sendBeacon(this.reportUrl, JSON.stringify(errorData))
64 : fetch(this.reportUrl, {
65 method: 'POST',
66 headers: { 'Content-Type': 'application/json' },
67 body: JSON.stringify(errorData),
68 });
69 } catch (err) {
70 console.warn('错误上报失败:', err);
71 }
72 }
73
74
75 captureException(error, context = {}) {
76 const errorInfo = {
77 type: 'manual-capture',
78 message: error.message,
79 stack: error.stack,
80 ...this.extraContext,
81 ...context,
82 };
83 this.report(errorInfo);
84 }
85}
86
87export default ErrorTracker;
🧪 使用方法
1. 初始化
1import ErrorTracker from './error-tracker';
2
3const tracker = new ErrorTracker({
4 reportUrl: 'https://your-domain.com/log-collector',
5 context: {
6 appVersion: '1.0.0',
7 userId: '123456',
8 },
9});
2. 手动捕获异常(适用于 try/catch 或自定义埋点)
1try {
2 throw new Error('业务异常');
3} catch (e) {
4 tracker.captureException(e, { module: 'UserProfile' });
5}
🛠️ 后端上报接口示例(Node.js)
1app.post('/report-error', express.json(), (req, res) => {
2 console.log('错误上报:', req.body);
3
4 res.status(200).send('ok');
5});
💡 可扩展建议
- 加入节流、去重机制(防止重复错误刷爆接口)
- 支持用户行为轨迹记录(如点击、页面跳转)
- 上报前过滤第三方脚本错误
- 使用 Source Map 解码堆栈信息
个人笔记记录 2021 ~ 2025