背景问题
在生产环境中,错误处理和监控是保证应用稳定性的重要环节。
方案思考
- 如何统一错误处理
- 如何收集错误信息
- 如何上报错误日志
具体实现
错误处理系统:
// utils/errorHandler.ts – 错误处理系统
import { App } from 'vue';
// 错误类型定义
interface ErrorInfo {
type: 'js' | 'resource' | 'promise' | 'ajax';
message: string;
stack?: string;
url?: string;
line?: number;
column?: number;
timestamp: number;
userAgent?: string;
customInfo?: any;
}
// 错误上报配置
interface ReportConfig {
url: string;
enable: boolean;
level: 'error' | 'warn' | 'info';
samplingRate?: number; // 采样率
}
class ErrorHandler {
private config: ReportConfig;
private queue: ErrorInfo[] = [];
private isReporting = false;
constructor(config: ReportConfig) {
this.config = {
samplingRate: 1,
…config
};
this.init();
}
private init() {
// 捕获JavaScript错误
window.addEventListener('error', this.handleError.bind(this));
// 捕获Promise错误
window.addEventListener('unhandledrejection', this.handlePromiseRejection.bind(this));
// 捕获资源加载错误
window.addEventListener('error', this.handleResourceError.bind(this), true);
}
private handleError(event: ErrorEvent) {
const errorInfo: ErrorInfo = {
type: 'js',
message: event.message,
stack: event.error?.stack,
url: event.filename,
line: event.lineno,
column: event.colno,
timestamp: Date.now(),
userAgent: navigator.userAgent
};
this.report(errorInfo);
}
private handlePromiseRejection(event: PromiseRejectionEvent) {
const errorInfo: ErrorInfo = {
type: 'promise',
message: event.reason?.message || String(event.reason),
stack: event.reason?.stack,
timestamp: Date.now(),
userAgent: navigator.userAgent
};
this.report(errorInfo);
}
private handleResourceError(event: ErrorEvent) {
if (event.target !== window) {
const target = event.target as HTMLScriptElement | HTMLLinkElement | HTMLImageElement;
const errorInfo: ErrorInfo = {
type: 'resource',
message: `Resource load error: ${target.src || target.href}`,
url: target.src || target.href,
timestamp: Date.now(),
userAgent: navigator.userAgent
};
this.report(errorInfo);
}
}
public report(errorInfo: ErrorInfo) {
// 采样率控制
if (Math.random() > (this.config.samplingRate || 1)) {
return;
}
// 级别过滤
if (this.shouldReport(errorInfo)) {
this.queue.push(errorInfo);
this.scheduleReport();
}
}
private shouldReport(errorInfo: ErrorInfo): boolean {
if (!this.config.enable) return false;
switch (this.config.level) {
case 'error':
return errorInfo.type === 'js' || errorInfo.type === 'promise';
case 'warn':
return true;
default:
return true;
}
}
private scheduleReport() {
if (this.isReporting) return;
this.isReporting = true;
setTimeout(() => {
this.flushQueue();
this.isReporting = false;
}, 1000);
}
private flushQueue() {
if (this.queue.length === 0) return;
const data = JSON.stringify(this.queue);
this.queue = [];
// 发送错误报告
if ('sendBeacon' in navigator) {
navigator.sendBeacon(this.config.url, data);
} else {
fetch(this.config.url, {
method: 'POST',
body: data,
headers: { 'Content-Type': 'application/json' }
}).catch(() => {
// 忽略上报错误
});
}
}
}
// 创建全局错误处理器实例
export const globalErrorHandler = new ErrorHandler({
url: '/api/errors/report',
enable: true,
level: 'error'
});
// 在main.ts中注册
export function registerErrorHandler(app: App) {
// 捕获组件错误
app.config.errorHandler = (err, instance, info) => {
// 上报错误
globalErrorHandler.report({
type: 'js',
message: err.message,
stack: err.stack,
timestamp: Date.now(),
customInfo: {
component: instance?.$options.name,
lifecycle: info
}
});
return false; // 阻止错误继续向上传播
};
return globalErrorHandler;
}
错误边界组件:
<!– components/ErrorBoundary.vue –>
<script setup lang="ts">
import { ref, onErrorCaptured } from 'vue';
const hasError = ref(false);
const errorMessage = ref('');
onErrorCaptured((err, instance, info) => {
hasError.value = true;
errorMessage.value = err.message;
// 上报错误
globalErrorHandler.report({
type: 'js',
message: err.message,
stack: err.stack,
timestamp: Date.now(),
customInfo: {
component: instance?.$options.name,
lifecycle: info
}
});
return false; // 阻止错误继续向上传播
});
const handleReset = () => {
hasError.value = false;
errorMessage.value = '';
};
</script>
<template>
<div v-if="!hasError">
<slot />
</div>
<div v-else class="error-boundary">
<h3>出错了!</h3>
<p>{{ errorMessage }}</p>
<button @click="handleReset">重试</button>
</div>
</template>
效果验证
通过统一的错误处理系统,可以有效地捕获、记录和上报各种类型的错误,提高应用的稳定性。
经验总结
错误处理应该是系统性的,需要从前端到后端建立完整的错误监控体系。
网硕互联帮助中心
![【完整源码+数据集+部署教程】快递盒检测检测系统源码 [一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/02/20260202054924-69803ae457a04-220x150.jpg)


评论前必须登录!
注册