export interface PerformanceThresholds { pageLoadTime: number; firstContentfulPaint: number; largestContentfulPaint: number; timeToInteractive: number; cumulativeLayoutShift: number; firstInputDelay: number; fps: number; frameTime: number; animationDuration: number; } export const PERFORMANCE_THRESHOLDS: PerformanceThresholds = { pageLoadTime: 2000, firstContentfulPaint: 1000, largestContentfulPaint: 1500, timeToInteractive: 2000, cumulativeLayoutShift: 0.1, firstInputDelay: 100, fps: 30, frameTime: 33.33, animationDuration: 500, }; export interface PerformanceMetrics { pageLoadTime: number; firstContentfulPaint: number; largestContentfulPaint: number; timeToInteractive: number; cumulativeLayoutShift: number; firstInputDelay: number; fps: number; frameTime: number; droppedFrames: number; totalFrames: number; animationDuration: number; } export interface PerformanceReport { timestamp: string; url: string; platform: string; metrics: PerformanceMetrics; thresholds: PerformanceThresholds; passed: boolean; issues: PerformanceIssue[]; } export interface PerformanceIssue { type: 'page-load' | 'animation' | 'rendering'; metric: string; actual: number; expected: number; severity: 'critical' | 'warning' | 'info'; message: string; } export class PerformanceMonitor { private metrics: PerformanceMetrics; private thresholds: PerformanceThresholds; private issues: PerformanceIssue[]; constructor(thresholds?: Partial) { this.thresholds = { ...PERFORMANCE_THRESHOLDS, ...thresholds }; this.metrics = this.initializeMetrics(); this.issues = []; } private initializeMetrics(): PerformanceMetrics { return { pageLoadTime: 0, firstContentfulPaint: 0, largestContentfulPaint: 0, timeToInteractive: 0, cumulativeLayoutShift: 0, firstInputDelay: 0, fps: 0, frameTime: 0, droppedFrames: 0, totalFrames: 0, animationDuration: 0, }; } public setMetric(key: keyof PerformanceMetrics, value: number): void { this.metrics[key] = value; } public getMetric(key: keyof PerformanceMetrics): number { return this.metrics[key]; } public getMetrics(): PerformanceMetrics { return { ...this.metrics }; } public checkThresholds(): void { this.issues = []; if (this.metrics.pageLoadTime > this.thresholds.pageLoadTime) { this.issues.push({ type: 'page-load', metric: 'pageLoadTime', actual: this.metrics.pageLoadTime, expected: this.thresholds.pageLoadTime, severity: 'critical', message: `页面加载时间 ${this.metrics.pageLoadTime}ms 超过阈值 ${this.thresholds.pageLoadTime}ms`, }); } if (this.metrics.firstContentfulPaint > this.thresholds.firstContentfulPaint) { this.issues.push({ type: 'page-load', metric: 'firstContentfulPaint', actual: this.metrics.firstContentfulPaint, expected: this.thresholds.firstContentfulPaint, severity: 'warning', message: `首次内容绘制 ${this.metrics.firstContentfulPaint}ms 超过阈值 ${this.thresholds.firstContentfulPaint}ms`, }); } if (this.metrics.largestContentfulPaint > this.thresholds.largestContentfulPaint) { this.issues.push({ type: 'page-load', metric: 'largestContentfulPaint', actual: this.metrics.largestContentfulPaint, expected: this.thresholds.largestContentfulPaint, severity: 'warning', message: `最大内容绘制 ${this.metrics.largestContentfulPaint}ms 超过阈值 ${this.thresholds.largestContentfulPaint}ms`, }); } if (this.metrics.timeToInteractive > this.thresholds.timeToInteractive) { this.issues.push({ type: 'page-load', metric: 'timeToInteractive', actual: this.metrics.timeToInteractive, expected: this.thresholds.timeToInteractive, severity: 'warning', message: `可交互时间 ${this.metrics.timeToInteractive}ms 超过阈值 ${this.thresholds.timeToInteractive}ms`, }); } if (this.metrics.cumulativeLayoutShift > this.thresholds.cumulativeLayoutShift) { this.issues.push({ type: 'rendering', metric: 'cumulativeLayoutShift', actual: this.metrics.cumulativeLayoutShift, expected: this.thresholds.cumulativeLayoutShift, severity: 'warning', message: `累积布局偏移 ${this.metrics.cumulativeLayoutShift} 超过阈值 ${this.thresholds.cumulativeLayoutShift}`, }); } if (this.metrics.firstInputDelay > this.thresholds.firstInputDelay) { this.issues.push({ type: 'page-load', metric: 'firstInputDelay', actual: this.metrics.firstInputDelay, expected: this.thresholds.firstInputDelay, severity: 'warning', message: `首次输入延迟 ${this.metrics.firstInputDelay}ms 超过阈值 ${this.thresholds.firstInputDelay}ms`, }); } if (this.metrics.fps < this.thresholds.fps) { this.issues.push({ type: 'animation', metric: 'fps', actual: this.metrics.fps, expected: this.thresholds.fps, severity: 'critical', message: `动画帧率 ${this.metrics.fps}fps 低于阈值 ${this.thresholds.fps}fps`, }); } if (this.metrics.frameTime > this.thresholds.frameTime) { this.issues.push({ type: 'animation', metric: 'frameTime', actual: this.metrics.frameTime, expected: this.thresholds.frameTime, severity: 'warning', message: `帧时间 ${this.metrics.frameTime}ms 超过阈值 ${this.thresholds.frameTime}ms`, }); } } public getIssues(): PerformanceIssue[] { return [...this.issues]; } public hasCriticalIssues(): boolean { return this.issues.some(issue => issue.severity === 'critical'); } public hasWarnings(): boolean { return this.issues.some(issue => issue.severity === 'warning'); } public generateReport(url: string, platform: string): PerformanceReport { this.checkThresholds(); return { timestamp: new Date().toISOString(), url, platform, metrics: this.getMetrics(), thresholds: this.thresholds, passed: !this.hasCriticalIssues(), issues: this.getIssues(), }; } public reset(): void { this.metrics = this.initializeMetrics(); this.issues = []; } } export const createPerformanceMonitor = (thresholds?: Partial): PerformanceMonitor => { return new PerformanceMonitor(thresholds); }; export const generatePerformanceReportSummary = (reports: PerformanceReport[]): string => { const totalReports = reports.length; const passedReports = reports.filter(report => report.passed).length; const failedReports = totalReports - passedReports; const totalIssues = reports.reduce((sum, report) => sum + report.issues.length, 0); const criticalIssues = reports.reduce((sum, report) => sum + report.issues.filter(issue => issue.severity === 'critical').length, 0); const warningIssues = reports.reduce((sum, report) => sum + report.issues.filter(issue => issue.severity === 'warning').length, 0); const avgPageLoadTime = reports.reduce((sum, report) => sum + report.metrics.pageLoadTime, 0) / totalReports; const avgFPS = reports.reduce((sum, report) => sum + report.metrics.fps, 0) / totalReports; return ` 性能测试报告摘要 ================ 测试时间: ${new Date().toISOString()} 测试数量: ${totalReports} 通过数量: ${passedReports} 失败数量: ${failedReports} 通过率: ${((passedReports / totalReports) * 100).toFixed(2)}% 问题统计 -------- 总问题数: ${totalIssues} 严重问题: ${criticalIssues} 警告问题: ${warningIssues} 性能指标 -------- 平均页面加载时间: ${avgPageLoadTime.toFixed(2)}ms 平均动画帧率: ${avgFPS.toFixed(2)}fps 详细信息 -------- ${reports.map((report, index) => ` 报告 ${index + 1}: ${report.url} - 平台: ${report.platform} - 状态: ${report.passed ? '通过' : '失败'} - 页面加载时间: ${report.metrics.pageLoadTime}ms - 动画帧率: ${report.metrics.fps}fps - 问题数: ${report.issues.length} ${report.issues.length > 0 ? ` 问题详情:\n${report.issues.map(issue => ` - [${issue.severity.toUpperCase()}] ${issue.message}`).join('\n')}` : ''} `).join('\n')} `; };