📖 核心概念
- Web Vitals核心指标
- Lighthouse审计
- 真实用户监控(RUM)
- 错误追踪与Sentry
- 日志系统
- 性能预算与告警
💻 代码实现
Web Vitals监控
✅ typescript
// Web Vitals上报
import { onCLS, onFID, onLCP, onFCP, onTTFB, onINP } from 'web-vitals';
type MetricName = 'CLS' | 'FID' | 'LCP' | 'FCP' | 'TTFB' | 'INP';
interface WebVitalMetric {
name: MetricName;
value: number;
rating: 'good' | 'needs-improvement' | 'poor';
delta: number;
navigationType: string;
}
function sendToAnalytics(metric: WebVitalMetric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
delta: metric.delta,
id: metric.name.toLowerCase() + '-' + Date.now(),
url: window.location.href,
userAgent: navigator.userAgent,
});
// 使用sendBeacon确保页面卸载时也能发送
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/analytics/web-vitals', body);
} else {
fetch('/api/analytics/web-vitals', { body, method: 'POST', keepalive: true });
}
}
// 注册所有指标
export function reportWebVitals() {
onCLS(sendToAnalytics);
onFID(sendToAnalytics);
onLCP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);
onINP(sendToAnalytics);
}
// app/layout.tsx
// import { ReportWebVitals } from '@/components/ReportWebVitals';
// <ReportWebVitals />
// API路由:接收并存储指标
// app/api/analytics/web-vitals/route.ts
export async function POST(request: Request) {
const metric = await request.json();
console.log('[Web Vitals]', metric.name, metric.value, metric.rating);
// 存储到数据库或发送到监控服务
return new Response('OK', { status: 200 });
}
错误追踪与Sentry
✅ typescript
// Sentry集成
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 0.1, // 10%的请求追踪
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0, // 出错时100%录制
integrations: [
Sentry.replayIntegration(),
Sentry.browserTracingIntegration(),
],
// 性能监控
tracePropagationTargets: ['localhost', /^https:\/\/myapp\.com/],
// 忽略已知错误
ignoreErrors: ['ResizeObserver loop', 'Network request failed'],
});
// 自定义错误边界
'use client';
import { Component, type ReactNode } from 'react';
import * as Sentry from '@sentry/nextjs';
interface Props { children: ReactNode; fallback?: ReactNode; }
interface State { hasError: boolean; error?: Error; }
export class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false };
static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
Sentry.captureException(error, { contexts: { react: { componentStack: errorInfo.componentStack } } });
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div className="error-boundary">
<h2>出了点问题</h2>
<button onClick={() => { this.setState({ hasError: false }); window.location.reload(); }}>
刷新页面
</button>
</div>
);
}
return this.props.children;
}
}
// 性能预算检查
const performanceBudget = {
LCP: { good: 2500, poor: 4000 }, // ms
FID: { good: 100, poor: 300 }, // ms
CLS: { good: 0.1, poor: 0.25 }, // score
FCP: { good: 1800, poor: 3000 }, // ms
TTFB: { good: 800, poor: 1800 }, // ms
bundleSize: { good: 200, poor: 400 }, // KB
};
function checkBudget(metric: string, value: number) {
const budget = performanceBudget[metric];
if (!budget) return 'info';
if (value <= budget.good) return '✅ good';
if (value <= budget.poor) return '⚠️ needs improvement';
return '❌ poor';
}
🔍 性能监控实战经验
监控的目的是发现问题和验证优化效果,而不是追求数字。关键经验:关注P75和P95而非平均值(平均值掩盖长尾问题)、区分首次加载和二次访问、注意移动端与桌面端的差异、监控要持续而非一次性。建立性能基线,在基线上做回归检测。
🎯 练习任务
- 1 集成Web Vitals监控,上报到后端
- 2 配置Sentry错误追踪,捕获前端异常
- 3 使用Lighthouse审计并优化你的应用
- 4 设置性能预算,在CI中检查关键指标
🏆 成就解锁
性能监控师 — 掌握前端性能监控体系,用数据驱动优化