在前端应用日益复杂的今天,性能监控已成为提升用户体验的关键环节。结合 TypeScript 的类型安全特性和浏览器原生 Performance API,我们可以构建出既健壮又高效的性能监控系统。本文将深入探讨主流的使用案例和最佳实践。
一、Performance API 与 TypeScript 基础
1.1 API 概览
Performance API 提供了高精度的时间测量能力,核心接口包括:
-
performance.now():微秒级时间戳 -
PerformanceObserver:异步监听性能事件 -
PerformanceNavigationTiming:页面导航耗时 -
PerformanceResourceTiming:资源加载详情
1.2 TypeScript 类型定义
首先定义基础数据结构和枚举类型,确保类型安全:
// 监控数据类型定义
export enum TraceTypeEnum {
performance = 'performance',
error = 'error'
}
export enum TraceSubTypeEnum {
resource = 'resource',
fcp = 'fcp',
lcp = 'lcp',
load = 'load'
}
export interface PerformanceMetric {
type: TraceTypeEnum.performance;
subType: TraceSubTypeEnum;
pageUrl: string;
timestamp: number;
[key: string]: any;
}
export interface ResourceTimingData extends PerformanceMetric {
name: string;
sourceType: string;
duration: number;
dns: number;
tcp: number;
ttfb: number;
responseBodySize: number;
}
二、主流实践案例
2.1 资源加载性能监控
使用 PerformanceObserver 捕获所有资源加载详情,这是现代浏览器推荐的方式:
export function initResourceTimingMonitor(reportUrl: string): void {
const host = new URL(reportUrl).host;
const entryHandler = (list: PerformanceObserverEntryList) => {
const entries = list.getEntries() as PerformanceResourceTiming[];
entries.forEach(entry => {
// 避免监控 SDK 自身请求导致的循环上报
if (entry.name.includes(host)) return;
const timingData: ResourceTimingData = {
type: TraceTypeEnum.performance,
subType: TraceSubTypeEnum.resource,
name: entry.name,
sourceType: entry.initiatorType,
duration: entry.duration,
dns: entry.domainLookupEnd - entry.domainLookupStart,
tcp: entry.connectEnd - entry.connectStart,
redirect: entry.redirectEnd - entry.redirectStart,
ttfb: entry.responseStart - entry.requestStart,
protocol: entry.nextHopProtocol,
responseBodySize: entry.encodedBodySize,
responseHeaderSize: entry.transferSize - entry.encodedBodySize,
transferSize: entry.transferSize,
resourceSize: entry.decodedBodySize,
startTime: entry.startTime,
pageUrl: window.location.href,
timestamp: Date.now()
};
// 批量上报
batchReport(timingData);
});
};
const observer = new PerformanceObserver(entryHandler);
observer.observe({ type: 'resource', buffered: true });
}
2.2 Web Vitals 核心指标监控
Google 的 Web Vitals 是评估用户体验的关键指标,包括 FCP、LCP、FID 等:
// 首次内容绘制 (FCP)
export function monitorFCP(): void {
const entryHandler = (list: PerformanceObserverEntryList) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-contentful-paint') {
const metricData: PerformanceMetric = {
type: TraceTypeEnum.performance,
subType: TraceSubTypeEnum.fcp,
value: entry.startTime,
pageUrl: window.location.href,
timestamp: Date.now()
};
batchReport(metricData);
observer.disconnect();
}
}
};
const observer = new PerformanceObserver(entryHandler);
observer.observe({ type: 'paint', buffered: true });
}
// 最大内容绘制 (LCP)
export function monitorLCP(): void {
let maxLCPValue = 0;
const entryHandler = (list: PerformanceObserverEntryList) => {
const entries = list.getEntries() as PerformanceEntry[];
for (const entry of entries) {
const { startTime, size, element } = entry as any;
if (startTime > maxLCPValue) {
maxLCPValue = startTime;
const lcpData: PerformanceMetric = {
type: TraceTypeEnum.performance,
subType: TraceSubTypeEnum.lcp,
value: startTime,
element: element?.tagName || '',
size,
pageUrl: window.location.href,
timestamp: Date.now()
};
batchReport(lcpData);
}
}
};
const observer = new PerformanceObserver(entryHandler);
observer.observe({ type: 'largest-contentful-paint', buffered: true });
}
2.3 自定义性能打点
在业务关键路径中插入自定义标记,精确测量特定操作耗时:
class PerformanceTracker {
private marks = new Map<string, number>();
// 开始标记
start(markName: string): void {
performance.mark(`start:${markName}`);
this.marks.set(markName, performance.now());
}
// 结束标记并计算耗时
end(markName: string): number {
const startTime = this.marks.get(markName);
if (!startTime) {
throw new Error(`Performance mark '${markName}' not found`);
}
const endTime = performance.now();
const duration = endTime - startTime;
performance.measure(markName, `start:${markName}`, `end:${markName}`);
// 上报自定义指标
const customMetric: PerformanceMetric = {
type: TraceTypeEnum.performance,
subType: 'custom' as any,
name: markName,
duration,
pageUrl: window.location.href,
timestamp: Date.now()
};
batchReport(customMetric);
return duration;
}
// 测量异步函数
async measureAsync<T>(name: string, fn: () => Promise<T>): Promise<T> {
this.start(name);
try {
const result = await fn();
return result;
} finally {
this.end(name);
}
}
}
// 使用示例
const tracker = new PerformanceTracker();
async function fetchUserData(): Promise<UserData> {
return tracker.measureAsync('fetchUserData', async () => {
const response = await fetch('/api/user');
return response.json();
});
}
2.4 页面加载完整耗时分析
综合使用 Navigation Timing API 分析页面加载全链路:
export function monitorNavigationTiming(): void {
// 优先使用 PerformanceObserver (Level 2)
try {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries() as PerformanceNavigationTiming[]) {
if (entry.type === 'navigation') {
const timingData = {
type: TraceTypeEnum.performance,
subType: 'navigation',
dnsTime: entry.domainLookupEnd - entry.domainLookupStart,
tcpTime: entry.connectEnd - entry.connectStart,
ttfb: entry.responseStart - entry.requestStart,
responseTime: entry.responseEnd - entry.responseStart,
domParseTime: entry.domContentLoadedEventStart - entry.responseEnd,
resourceLoadTime: entry.loadEventStart - entry.domContentLoadedEventEnd,
totalLoadTime: entry.loadEventEnd - entry.fetchStart,
redirectCount: entry.redirectCount,
pageUrl: window.location.href,
timestamp: Date.now()
};
batchReport(timingData);
}
}
});
observer.observe({ type: 'navigation', buffered: true });
} catch {
// 降级方案:使用 performance.timing (Level 1)
window.addEventListener('load', () => {
setTimeout(() => { // 确保 loadEventEnd 已记录
const timing = performance.timing;
const navigationData = {
type: TraceTypeEnum.performance,
subType: 'navigation',
dnsTime: timing.domainLookupEnd - timing.domainLookupStart,
tcpTime: timing.connectEnd - timing.connectStart,
ttfb: timing.responseStart - timing.requestStart,
totalLoadTime: timing.loadEventEnd - timing.fetchStart,
pageUrl: window.location.href,
timestamp: Date.now()
};
batchReport(navigationData);
}, 100);
});
}
}
2.5 批量上报策略
实现高效的数据上报机制,减少网络请求:
class MonitorSDK {
private queue: any[] = [];
private timeoutId: NodeJS.Timeout | null = null;
private readonly config = {
maxBatchSize: 10,
maxWaitTime: 5000,
reportUrl: '/api/performance'
};
addToQueue(data: any): void {
this.queue.push(data);
// 达到批量大小立即上报
if (this.queue.length >= this.config.maxBatchSize) {
this.flushQueue();
} else if (!this.timeoutId) {
// 否则延迟上报
this.timeoutId = setTimeout(() => {
this.flushQueue();
}, this.config.maxWaitTime);
}
}
private flushQueue(): void {
if (this.queue.length === 0) return;
const batchData = [...this.queue];
this.queue = [];
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
// 使用 navigator.sendBeacon 确保页面卸载时也能上报
if (navigator.sendBeacon) {
navigator.sendBeacon(
this.config.reportUrl,
JSON.stringify({ batch: batchData })
);
} else {
fetch(this.config.reportUrl, {
method: 'POST',
body: JSON.stringify({ batch: batchData }),
headers: { 'Content-Type': 'application/json' },
keepalive: true
}).catch(console.error);
}
}
}
三、高级应用技巧
3.1 Service Worker 性能监控
结合 Service Worker 实现离线缓存和性能优化:
// 在 Service Worker 中监控缓存性能
self.addEventListener('fetch', (event: FetchEvent) => {
const startTime = performance.now();
event.respondWith(
caches.match(event.request).then(cachedResponse => {
if (cachedResponse) {
// 从缓存读取
const duration = performance.now() - startTime;
reportToAnalytics({
type: 'cache-hit',
url: event.request.url,
duration
});
return cachedResponse;
}
// 网络请求并缓存
return fetch(event.request).then(response => {
const duration = performance.now() - startTime;
reportToAnalytics({
type: 'network-fetch',
url: event.request.url,
duration
});
const clone = response.clone();
caches.open('v1').then(cache => cache.put(event.request, clone));
return response;
});
})
);
});
3.2 Web Workers 性能隔离
将耗时操作放到 Web Worker 中,避免阻塞主线程:
// main.ts
const worker = new Worker('./performance-worker.ts');
worker.postMessage({ type: 'start-heavy-task' });
worker.onmessage = (event: MessageEvent) => {
const { type, duration, data } = event.data;
if (type === 'task-complete') {
console.log(`任务耗时: ${duration}ms`);
// 上报性能数据
batchReport({
type: TraceTypeEnum.performance,
subType: 'worker-task',
duration,
timestamp: Date.now()
});
}
};
// performance-worker.ts
self.addEventListener('message', (event) => {
if (event.data.type === 'start-heavy-task') {
const start = performance.now();
// 执行耗时计算
const result = heavyComputation();
const duration = performance.now() - start;
self.postMessage({
type: 'task-complete',
duration,
data: result
});
}
});
3.3 3D 游戏性能监控案例
参考 Three.js 游戏项目的性能优化实践:
class GamePerformanceMonitor {
private frameCount = 0;
private lastFpsUpdate = performance.now();
private fps = 0;
monitorRenderLoop(renderer: THREE.WebGLRenderer): void {
const monitor = () => {
this.frameCount++;
const now = performance.now();
// 每秒计算一次 FPS
if (now >= this.lastFpsUpdate + 1000) {
this.fps = Math.round(
(this.frameCount * 1000) / (now - this.lastFpsUpdate)
);
if (this.fps < 30) {
// 性能警告
console.warn(`低 FPS 警告: ${this.fps}`);
}
// 上报性能指标
batchReport({
type: TraceTypeEnum.performance,
subType: 'fps',
value: this.fps,
timestamp: Date.now()
});
this.frameCount = 0;
this.lastFpsUpdate = now;
}
requestAnimationFrame(monitor);
};
monitor();
}
// 监控特效渲染性能
monitorEffect(effectName: string, renderFn: () => void): void {
const start = performance.now();
renderFn();
const duration = performance.now() - start;
if (duration > 16.67) { // 超过一帧时间(60fps)
console.warn(`特效 ${effectName} 渲染耗时: ${duration.toFixed(2)}ms`);
}
}
}
四、性能优化最佳实践
4.1 避免监控本身影响性能
-
采样率控制:只收集部分用户的数据
const shouldMonitor = Math.random() < 0.1; // 10% 采样
if (!shouldMonitor) return;
-
防抖与节流:对频繁触发的事件进行限流
import { debounce } from 'lodash';
const debouncedReport = debounce(batchReport, 1000, {
maxWait: 5000
});
-
非阻塞上报:使用
requestIdleCallback延迟上报
if ('requestIdleCallback' in window) {
requestIdleCallback(() => flushQueue());
} else {
setTimeout(() => flushQueue(), 0);
}
4.2 类型守卫确保运行时安全
function isPerformanceEntry(entry: any): entry is PerformanceEntry {
return entry &&
typeof entry.name === 'string' &&
typeof entry.startTime === 'number';
}
function processEntry(entry: unknown): void {
if (!isPerformanceEntry(entry)) {
console.warn('Invalid performance entry');
return;
}
// TypeScript 现在知道 entry 是 PerformanceEntry 类型
console.log(entry.startTime);
}
4.3 常量枚举优化性能
使用 const enum 减少编译后代码体积:
const enum MetricType {
FCP = 'first-contentful-paint',
LCP = 'largest-contentful-paint',
LOAD = 'load'
}
// 编译后直接使用字符串字面量,无运行时开销
if (entry.name === MetricType.FCP) {
// ...
}
五、实战:完整监控 SDK 集成
5.1 初始化配置
interface MonitorConfig {
appId: string;
appVersion: string;
reportUrl: string;
sampleRate?: number;
enableWebVitals?: boolean;
enableResourceTiming?: boolean;
}
class PerformanceMonitorSDK {
private config: Required<MonitorConfig>;
private initialized = false;
constructor(options: MonitorConfig) {
this.config = {
sampleRate: 1,
enableWebVitals: true,
enableResourceTiming: true,
...options
};
}
init(): void {
if (this.initialized) {
console.warn('Monitor SDK already initialized');
return;
}
// 采样控制
if (Math.random() > this.config.sampleRate) {
console.log('Performance monitoring skipped due to sample rate');
return;
}
// 初始化各项监控
this.config.enableWebVitals && this.initWebVitals();
this.config.enableResourceTiming && initResourceTimingMonitor(this.config.reportUrl);
// 页面卸载时确保数据上报
window.addEventListener('beforeunload', () => {
this.flushQueue();
});
this.initialized = true;
console.log('Performance Monitor SDK initialized');
}
}
5.2 实际应用案例
// 在项目入口文件中初始化
const monitor = new PerformanceMonitorSDK({
appId: 'my-web-app',
appVersion: '2.1.0',
reportUrl: 'https://analytics.example.com/api/performance',
sampleRate: 0.5, // 50% 用户采样
enableWebVitals: true,
enableResourceTiming: true
});
monitor.init();
// 业务代码中打点
const tracker = new PerformanceTracker();
async function bootApplication() {
await tracker.measureAsync('app-bootstrap', async () => {
// 初始化路由
// 加载用户配置
// 渲染根组件
});
}
六、总结与展望
通过 TypeScript 与 Performance API 的结合,我们可以:
-
构建类型安全的性能监控系统,减少运行时错误
-
精确测量业务关键路径,定位性能瓶颈
-
实现自动化数据上报,持续监控应用健康状况
-
优化用户体验,基于真实数据驱动改进
未来发展趋势:
-
与 OpenTelemetry 集成,实现前后端全链路追踪
-
AI 驱动的性能分析,自动识别异常模式
-
RUM (Real User Monitoring) 与 Synthetic Monitoring 的深度结合
1237

被折叠的 条评论
为什么被折叠?



