web-see-monitor
v1.0.0
Published
前端监控 SDK,包含性能监控、错误监控和用户行为监控
Maintainers
Readme
Web Monitor SDK
一个轻量级的前端监控 SDK,用于收集网页性能指标、错误信息和用户行为数据。
特性
- 🚀 轻量级,零依赖
- 📊 性能监控
- 首屏加载时间
- FCP (First Contentful Paint)
- LCP (Largest Contentful Paint)
- FID (First Input Delay)
- CLS (Cumulative Layout Shift)
- 🐛 错误监控
- JS 运行时错误
- Promise 未捕获异常
- 资源加载错误
- HTTP 请求异常
- 👨💻 用户行为跟踪
- PV/UV 统计
- 用户点击行为
- 页面停留时间
- 路由变化
安装
npm install web-monitor-sdk核心功能
1. 性能监控
页面加载性能
- 首屏加载时间(FP)
- 首次内容绘制(FCP)
- 最大内容绘制(LCP)
- 首次输入延迟(FID)
- 累积布局偏移(CLS)
资源加载性能
- 资源加载时间统计
- 资源加载瀑布图
- 静态资源缓存命中率
接口性能
- API 请求耗时统计
- 接口成功/失败率
- 慢请求追踪
2. 错误监控
JS 错误
- 运行时错误捕获
- Promise 异常监控
- 跨域脚本错误处理
资源错误
- 图片/脚本加载失败
- HTTP 404/500 等状态码监控
- 接口超时监控
控制台错误
- console.error 监控
- 自定义错误上报
3. 用户行为分析
- 页面访问
- PV/UV 统计
- 访问来源分析
- 停留时长统计
- 用户交互
- 点击行为追踪
- 滚动深度分析
- 表单操作记录
优化措施
1. 数据上报优化
批量上报
- 采用队列缓存,批量上报减少请求次数
- 支持配置上报阈值和时间间隔
- 页面卸载前确保数据发送完成
数据压缩
- 采用 JSON 压缩算法
- 大数据量采用分片上传
- 支持 gzip 压缩
2. 性能优化
采样控制
- 支持按比例采样上报
- 重要用户/错误数据优先上报
- 动态调整采样率
节流与防抖
- 高频事件节流处理
- 连续错误防抖优化
- 避免重复上报
3. 稳定性优化
容错处理
- SDK 异常自动恢复
- 关键流程容错
- 上报失败重试机制
浏览器兼容
- 优雅降级处理
- Polyfill 按需加载
- 主流浏览器全覆盖
实现细节
1. 性能数据采集
- 使用 Performance API 采集加载性能
- 使用 PerformanceObserver 监听性能事件
- 通过 requestAnimationFrame 计算渲染性能
2. 错误监控实现
- window.onerror 捕获 JS 运行时错误
- unhandledrejection 事件捕获 Promise 异常
- 重写 XMLHttpRequest 和 fetch 监控接口异常
3. 行为数据收集
- 使用事件委托优化点击监听
- 通过 MutationObserver 监控 DOM 变化
- 路由监听支持 hash 和 history 模式
4. 上报优化实现
- 使用 requestIdleCallback 在空闲时段上报
- 采用 Navigator.sendBeacon 处理页面卸载场景
- IndexedDB 存储离线数据 // ... 前面内容保持不变 ...
优化措施及实现
1. 数据上报优化
批量上报实现
class Reporter {
private queue: any[] = [];
private timer: number | null = null;
private readonly threshold = 10; // 队列阈值
private readonly interval = 5000; // 上报间隔
push(data: any) {
this.queue.push(data);
if (this.queue.length >= this.threshold) {
this.flush();
} else {
this.setupTimer();
}
}
private setupTimer() {
if (!this.timer) {
this.timer = window.setTimeout(() => this.flush(), this.interval);
}
}
private flush() {
if (this.queue.length === 0) return;
// 使用 sendBeacon 保证页面卸载时数据发送
const data = JSON.stringify(this.queue);
navigator.sendBeacon('/collect', data);
this.queue = [];
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
}数据压缩实现
class DataCompressor {
compress(data: any) {
// 1. 移除冗余字段
const cleaned = this.removeRedundant(data);
// 2. 数据编码压缩
const compressed = this.encode(cleaned);
// 3. 大数据分片处理
return this.slice(compressed);
}
private encode(data: any) {
// 短字符串映射
const dictionary: Record<string, string> = {
'error': 'e',
'warning': 'w',
'info': 'i',
'performance': 'p'
};
return JSON.stringify(data).replace(
/("error"|"warning"|"info"|"performance")/g,
match => dictionary[match.replace(/"/g, '')] || match
);
}
}2. 性能优化实现
采样控制实现
class SamplingController {
private readonly defaultRate = 0.1; // 默认采样率10%
shouldReport(data: any): boolean {
// 错误数据100%采样
if (data.type === 'error') return true;
// VIP用户100%采样
if (data.userType === 'vip') return true;
// 其他数据按比例采样
return Math.random() < this.getRate(data.type);
}
private getRate(type: string): number {
const rateMap = {
performance: 0.1,
behavior: 0.05,
custom: 0.01
};
return rateMap[type] || this.defaultRate;
}
}节流防抖实现
function debounce<T extends (...args: any[]) => any>(
fn: T,
wait: number
): (...args: Parameters<T>) => void {
let timer: number | null = null;
return function(...args: Parameters<T>) {
if (timer) clearTimeout(timer);
timer = window.setTimeout(() => {
fn.apply(this, args);
timer = null;
}, wait);
};
}
// 错误上报防抖
const reportError = debounce((error: Error) => {
reporter.send({
type: 'error',
message: error.message,
stack: error.stack
});
}, 2000);3. 稳定性优化实现
容错处理实现
class ErrorBoundary {
private retryTimes = 3;
async sendData(data: any) {
for (let i = 0; i < this.retryTimes; i++) {
try {
await this.send(data);
return;
} catch (err) {
if (i === this.retryTimes - 1) {
// 存储到 IndexedDB 待后续上报
await this.saveToIndexedDB(data);
}
// 指数退避重试
await this.sleep(Math.pow(2, i) * 1000);
}
}
}
private async saveToIndexedDB(data: any) {
const db = await this.openDB();
const tx = db.transaction('reports', 'readwrite');
await tx.objectStore('reports').add(data);
}
}浏览器兼容实现
class BrowserAdapter {
// Performance API 兼容
getFirstPaint() {
if (window.performance && window.performance.getEntriesByType) {
const [entry] = performance.getEntriesByType('paint');
return entry ? entry.startTime : null;
}
// 降级方案
return this.getFallbackTiming();
}
// 优雅降级
private getFallbackTiming() {
if (performance.timing) {
return performance.timing.domLoading - performance.timing.navigationStart;
}
return Date.now(); // 最后的降级方案
}
}4. 离线存储实现
class OfflineStorage {
private readonly DB_NAME = 'monitor_sdk';
private readonly STORE_NAME = 'offline_data';
async init() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.DB_NAME, 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
db.createObjectStore(this.STORE_NAME, {
keyPath: 'id',
autoIncrement: true
});
};
});
}
async saveOfflineData(data: any) {
const db = await this.init();
return new Promise((resolve, reject) => {
const tx = (db as IDBDatabase)
.transaction(this.STORE_NAME, 'readwrite');
const store = tx.objectStore(this.STORE_NAME);
const request = store.add({
data,
timestamp: Date.now()
});
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
}使用示例
const monitor = new WebMonitor({
appId: 'your-app-id',
// 开启性能监控
enablePerformance: true,
// 开启错误监控
enableError: true,
// 采样率配置
sampling: {
performance: 0.1,
behavior: 0.05
},
// 上报配置
report: {
threshold: 10,
interval: 5000
}
});
// 启动监控
monitor.start();这些优化措施的实现代码展示了:
- 如何实现批量上报和数据压缩
- 采样控制的具体逻辑
- 防抖节流的实现方式
- 错误重试和容错处理
- 浏览器兼容性处理
- 离线数据存储的实现
通过这些实现,可以确保监控 SDK 在各种场景下都能稳定高效地运行。
