前端错误收集 SDK 开发技术报告
- 基于 Monorepo + TypeScript 的现代化架构设计与实现指南**
- 日期:2025年10月28日
1. 背景与目标
随着前端应用复杂度的不断提升,错误监控与收集已成为保障用户体验和系统稳定性的核心能力。 构建一个可靠、轻量、可扩展的前端错误收集 SDK,能够帮助团队快速定位线上问题、提升研发效率。
本报告旨在指导你使用 Monorepo 架构 + TypeScript 开发一个现代化的前端错误收集 SDK, 涵盖架构设计、模块划分、错误捕获、数据上报、性能优化及最佳实践。
2. 技术选型与架构设计
2.1 核心技术栈
- 语言:TypeScript(强类型保障,提升 SDK 可维护性)
- 架构:Monorepo(使用 Turborepo 或 Nx 管理多包)
- 构建工具:Vite 或 Rollup(支持多格式输出:ESM、CJS、UMD)
- 包管理:pnpm(高效依赖管理,天然支持 Monorepo)
- 测试:Jest + Playwright(单元测试 + 端到端模拟)
- CI/CD:GitHub Actions(自动化测试、构建、发布)
2.2 Monorepo 包结构设计
text
packages/
├── core/ # 核心逻辑:错误捕获、上下文管理、上报机制
├── browser/ # 浏览器环境适配层(DOM 事件绑定、Performance API)
├── react/ # React 错误边界集成(可选)
├── vue/ # Vue 全局错误处理器集成(可选)
├── utils/ # 公共工具函数(防抖、节流、序列化、UUID 生成等)
├── types/ # 全局类型定义(TS 类型共享)
├── plugin-*/ # 插件体系(如 source-map 解析、用户行为追踪)
└── demo/ # 示例应用,用于本地调试与集成测试优势:模块解耦、独立发布、共享类型、统一构建流程。
3. 核心功能设计
3.1 错误捕获能力
3.1.1 全局异常捕获
window.onerror:捕获 JavaScript 运行时错误window.addEventListener('unhandledrejection'):捕获未处理的 Promise 异常console.error代理(可选):捕获开发者主动输出的错误
ts
// packages/core/src/errorHandler.ts
export function setupGlobalErrorHandler() {
window.onerror = (message, source, lineno, colno, error) => {
reportError({
type: 'runtime',
message,
stack: error?.stack,
filename: source,
line: lineno,
column: colno,
timestamp: Date.now(),
});
return true;
};
window.addEventListener('unhandledrejection', (event) => {
reportError({
type: 'promise',
message: event.reason?.message || String(event.reason),
stack: event.reason?.stack,
timestamp: Date.now(),
});
});
}3.1.2 资源加载错误
ts
// 捕获 <script>、<img> 等资源加载失败
window.addEventListener('error', (event) => {
if (event.target && (event.target as HTMLElement).src || (event.target as HTMLElement).href) {
reportError({
type: 'resource',
target: (event.target as HTMLElement).tagName,
url: (event.target as any).src || (event.target as any).href,
});
}
}, true);3.2 上下文信息收集
为错误提供丰富的上下文,便于定位问题:
| 信息类型 | 数据来源 |
|---|---|
| 用户信息 | UID、设备 ID(可匿名) |
| 设备信息 | UserAgent、屏幕尺寸、网络类型 |
| 页面信息 | URL、referrer、title |
| 性能指标 | FP、LCP、FID(通过 Performance API) |
| 自定义标签 | 业务自定义 metadata |
ts
interface ErrorEvent {
message: string;
stack?: string;
type: 'runtime' | 'promise' | 'resource';
context: {
url: string;
userAgent: string;
timestamp: number;
performance?: Record<string, number>;
custom?: Record<string, any>;
};
}3.3 数据上报策略
3.3.1 上报方式
- Navigator.sendBeacon:页面卸载时可靠上报(推荐)
- fetch + keepalive:现代浏览器支持
- Image Ping:兼容性最好,但无状态反馈
ts
export function sendReport(data: ErrorEvent) {
if (navigator.sendBeacon) {
navigator.sendBeacon('/sdk/report', new Blob([JSON.stringify(data)], { type: 'application/json' }));
} else {
fetch('/sdk/report', {
method: 'POST',
body: JSON.stringify(data),
keepalive: true,
headers: { 'Content-Type': 'application/json' }
}).catch(() => {
// fallback to Image ping
});
}
}3.3.2 上报优化
- 节流与防抖:避免短时间内大量上报
- 队列缓存:网络异常时暂存 localStorage
- 采样率控制:支持按比例采样(如 10%)
3.4 Source Map 支持(可选插件)
- 在构建时上传 Source Map 到服务端
- SDK 上报
sourcemapUrl和stack - 服务端解析原始代码位置(需后端配合)
4. SDK 初始化与配置
ts
import { init } from '@your-sdk/core';
init({
appId: 'your-app-id',
reportUrl: 'https://monitor.your-domain.com',
sampleRate: 0.1, // 10% 采样
enableConsoleCapture: false,
beforeSend: (event) => {
// 可修改或过滤事件
if (event.message.includes('third-party-error')) return null;
return event;
},
plugins: [sourceMapPlugin, userActionPlugin]
});5. TypeScript 最佳实践
5.1 类型共享
在 packages/types/ 中定义全局类型:
ts
// packages/types/index.ts
export interface SDKOptions {
appId: string;
reportUrl: string;
sampleRate?: number;
beforeSend?: (event: ErrorEvent) => ErrorEvent | null;
plugins?: Plugin[];
}
export interface ErrorEvent {
message: string;
stack?: string;
type: ErrorType;
context: Record<string, any>;
}其他包通过 @your-sdk/types 引入,避免重复定义。
5.2 严格模式
- 启用
strict: true、noImplicitAny、strictNullChecks - 使用
const assertions保证字面量类型
6. 构建与发布
6.1 多格式输出
使用 Rollup 或 Vite 构建:
dist/esm/index.js—— 供现代浏览器/打包工具使用dist/cjs/index.js—— 兼容 CommonJSdist/umd/index.js—— 全局变量挂载(window.YourSDK)
6.2 自动化发布
- 使用
changesets管理版本与 changelog - CI 流程自动发布到私有 npm 或公共 registry
7. 测试策略
| 测试类型 | 工具 | 目标 |
|---|---|---|
| 单元测试 | Jest | 核心逻辑、工具函数 |
| 集成测试 | Playwright | 模拟真实浏览器错误上报 |
| 类型测试 | dtslint | 验证 .d.ts 文件正确性 |
| 兼容性测试 | BrowserStack | 多浏览器验证 |
8. 安全与隐私
- 禁止收集敏感信息:如密码、token、个人身份信息(PII)
- 支持 GDPR:提供
disableTracking()API - CSP 兼容:上报域名白名单配置
- HTTPS 强制:仅允许安全上下文上报
9. 性能考量
- 启动性能:SDK 初始化应异步、非阻塞
- 内存占用:避免闭包泄漏,及时清理事件监听
- 体积控制:Gzip 后 < 5KB(核心包)
- Tree-shaking:确保模块化设计,支持按需引入
10. 扩展性设计
插件机制
ts
interface Plugin {
name: string;
init: (sdk: SDK) => void;
beforeReport?: (event: ErrorEvent) => ErrorEvent | null;
}
// 示例:用户行为插件
const userActionPlugin: Plugin = {
name: 'user-action',
init(sdk) {
document.addEventListener('click', (e) => {
sdk.addBreadcrumb({ type: 'click', target: e.target });
});
}
};11. 总结与建议
你正在构建的不仅仅是一个 SDK,而是一个前端可观测性基础设施。建议:
- 先做 MVP:从
core+browser包开始,实现基本错误捕获与上报 - 渐进式增强:逐步添加 React/Vue 集成、插件系统
- 文档驱动开发:为每个包编写 README 和 API 文档
- 监控 SDK 自身:记录 SDK 初始化失败、上报失败率
附录:推荐工具链
- Monorepo 管理:Turborepo + pnpm
- 构建:Vite(快)或 Rollup(成熟)
- 类型检查:tsc + eslint
- 版本管理:Changesets
- 发布:GitHub Actions + npm publish