前端监控是确保应用性能和用户体验的关键,主要涵盖以下核心指标,通常分为几个维度:
一、性能指标(Performance)
- 首次内容绘制 (FCP - First Contentful Paint)
- 浏览器首次渲染任何文本、图片或非空白 Canvas/SVG 的时间。
- 目标:< 1.8 秒(良好)
js
const getFCP = () => {
// 1. 使用 PerformanceObserver 监听 paint 类型条目
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-contentful-paint') {
// 2. 获取 FCP 时间(单位:毫秒)
const fcp = entry.startTime;
console.log('FCP:', fcp);
// 3. 上报数据(示例)
sendToAnalytics({ metric: 'FCP', value: fcp });
// 4. 停止监听
observer.disconnect();
break;
}
}
});
// 5. 开始监听 paint 类型的性能条目
observer.observe({ type: 'paint', buffered: true });
};
// 调用函数开始采集
getFCP();
- 最大内容绘制 (LCP - Largest Contentful Paint)
- 视口中最大可见元素(如图片、标题文本块)的渲染时间。
- 目标:< 2.5 秒(良好)
js
const getLCP = () => {
let maxLcpValue = 0;
const lcpEntries = [];
// 1. 创建观察者
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
// 2. 记录所有 LCP 候选元素
lcpEntries.push(entry);
// 3. 更新最大 LCP 值(取 renderTime 或 loadTime)
const time = entry.renderTime || entry.loadTime;
if (time > maxLcpValue) {
maxLcpValue = time;
}
});
});
// 4. 开始监听 LCP 条目
observer.observe({ type: 'largest-contentful-paint', buffered: true });
// 5. 页面加载完成时确定最终 LCP
window.addEventListener('load', () => {
// 延迟确保捕获所有元素
setTimeout(() => {
observer.disconnect();
// 6. 取最后一个有效条目作为最终 LCP(根据规范)
const finalEntry = lcpEntries[lcpEntries.length - 1];
const lcp = finalEntry?.renderTime || maxLcpValue;
console.log('LCP:', lcp);
sendToAnalytics({ metric: 'LCP', value: lcp });
}, 0);
});
// 7. 用户交互后停止记录(规范要求)
['click', 'keydown', 'scroll'].forEach(event => {
window.addEventListener(event, () => observer.disconnect(), { once: true });
});
};
getLCP();
- 首次输入延迟 (FID - First Input Delay)
- 用户首次交互(点击、输入等)到浏览器实际响应的时间差。
- 目标:< 100 毫秒(良好)
js
const getFID = () => {
// 1. 记录首次交互时间
let firstInteractionTime = performance.now();
let hasReported = false;
// 2. 监听首次输入事件
const listener = (event) => {
// 3. 跳过非用户主动交互(如滚动)
if (event.cancelable) {
const delay = event.processingStart - event.startTime;
console.log('FID:', delay);
sendToAnalytics({ metric: 'FID', value: delay });
// 4. 移除监听
window.removeEventListener('mousedown', listener);
window.removeEventListener('keydown', listener);
hasReported = true;
}
};
// 5. 通过 PerformanceObserver 捕获 first-input 条目
const observer = new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
if (entry.entryType === 'first-input' && !hasReported) {
const delay = entry.processingStart - entry.startTime;
console.log('FID (via Observer):', delay);
sendToAnalytics({ metric: 'FID', value: delay });
hasReported = true;
}
});
});
observer.observe({ type: 'first-input', buffered: true });
// 6. 降级方案:手动监听事件
window.addEventListener('mousedown', listener, { once: true, capture: true });
window.addEventListener('keydown', listener, { once: true, capture: true });
};
getFID();****
- 累积布局偏移 (CLS - Cumulative Layout Shift)
- 衡量页面渲染期间视觉稳定性(元素意外偏移的频率和幅度)。
- 目标:< 0.1(良好)
js
const getCLS = () => {
let sessionValue = 0;
let sessionEntries = [];
let clsValue = 0;
// 1. 布局偏移监听器
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
// 2. 忽略用户操作后500ms内的偏移(规范要求)
if (!entry.hadRecentInput) {
const lastEntry = sessionEntries[sessionEntries.length - 1];
// 3. 检查是否同一会话(1秒内连续偏移)
if (sessionEntries.length === 0 ||
entry.startTime - lastEntry.startTime < 1000) {
sessionValue += entry.value;
} else {
sessionValue = entry.value;
sessionEntries = [];
}
// 4. 记录当前条目
sessionEntries.push(entry);
// 5. 更新最大会话值
if (sessionValue > clsValue) {
clsValue = sessionValue;
}
}
});
});
// 6. 开始监听布局偏移
observer.observe({ type: 'layout-shift', buffered: true });
// 7. 页面隐藏时上报最终CLS
const reportCLS = () => {
observer.disconnect();
console.log('CLS:', clsValue);
sendToAnalytics({ metric: 'CLS', value: clsValue });
};
// 8. 在页面卸载或隐藏时报告
window.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') reportCLS();
});
window.addEventListener('pagehide', reportCLS);
};
getCLS();
首字节时间 (TTFB - Time to First Byte)
- 从发起请求到收到服务器响应的第一个字节的时间,反映服务器响应速度。
页面完全加载时间 (Load Time)
- 从导航开始到
window.onload
事件触发的时间。
- 从导航开始到
二、 错误监控(Errors)
- JavaScript 运行时错误
- 通过
window.onerror
或addEventListener('error')
捕获的未处理异常。
- 通过
- 资源加载失败
- 图片、脚本、CSS 等加载失败(通过
addEventListener('error')
捕获)。
- 图片、脚本、CSS 等加载失败(通过
- Promise 未捕获异常
- 通过
unhandledrejection
事件监控。
- 通过
- 接口请求错误
- HTTP 状态码(4xx/5xx)、网络超时等。
三、 用户行为与体验(UX)
- 页面浏览量(PV)与独立访客(UV)
- 用户交互路径
- 按钮点击、表单提交、路由跳转等关键操作。
- 白屏率
- 页面完全无内容渲染的比例。
- 卡顿率
- 页面帧率(FPS)低于 60 帧/秒的持续时间占比。
四、 接口监控(API)
- 请求成功率
- 成功响应(2xx/3xx)的请求比例。
- 请求耗时分布
- P50、P90、P99 分位的接口响应时间。
- 慢请求比例
- 超过设定阈值(如 2 秒)的请求占比。
- 错误类型分布
- 4xx(客户端错误) vs 5xx(服务端错误)。
五、 资源性能(Assets)
- CDN 资源加载速度
- JS、CSS、图片等静态资源的加载耗时。
- 资源缓存命中率
- 利用浏览器缓存(
memory-cache
/disk-cache
)的比例。
- 利用浏览器缓存(
六、 业务自定义指标
- 关键流程转化率
- 如注册、支付流程的完成率。
- 功能使用率
- 特定功能模块的点击/使用频率。
- 用户停留时长
- 页面或应用的单次使用时长。
监控工具示例
- 开源方案:Sentry(错误监控)、Prometheus + Grafana(性能指标)、Elastic APM
- 商业平台:Datadog、New Relic、阿里云 ARMS、腾讯云前端性能监控
- 标准协议:使用 W3C Performance Timeline API 采集性能数据
关键实践建议
- 核心 Web 指标(Web Vitals)优先:聚焦 LCP、FID、CLS 三大用户体验指标。
- 错误聚合与告警:对高频错误实时告警,避免影响范围扩大。
- 用户会话追踪(Session Replay):复现用户操作路径,辅助定位问题。
- 性能采样与聚合:全量监控可能消耗性能,合理采样降低开销。
💡 监控的核心目标是 快速发现问题 → 定位根因 → 驱动优化。建议结合业务场景选择关键指标,避免过度监控。