性能监控和优化是确保 Tailwind CSS 项目高效运行的关键。本节将详细介绍如何实施性能监控和进行相应的优化。

性能指标监控

Web Vitals 监控

  1. // utils/vitals.ts
  2. import { getCLS, getFID, getLCP, getTTFB, getFCP } from 'web-vitals';
  3. const reportWebVitals = (onPerfEntry: (metric: any) => void) => {
  4. if (onPerfEntry && onPerfEntry instanceof Function) {
  5. getCLS(onPerfEntry); // Cumulative Layout Shift
  6. getFID(onPerfEntry); // First Input Delay
  7. getLCP(onPerfEntry); // Largest Contentful Paint
  8. getTTFB(onPerfEntry); // Time to First Byte
  9. getFCP(onPerfEntry); // First Contentful Paint
  10. }
  11. };
  12. export const setupVitalsReporting = () => {
  13. reportWebVitals((metric) => {
  14. // 发送到分析服务
  15. console.log(metric);
  16. // 可以发送到 Google Analytics
  17. const analytics = (window as any).ga;
  18. if (analytics) {
  19. analytics('send', 'event', {
  20. eventCategory: 'Web Vitals',
  21. eventAction: metric.name,
  22. eventValue: Math.round(metric.value),
  23. eventLabel: metric.id,
  24. nonInteraction: true,
  25. });
  26. }
  27. });
  28. };

自定义性能监控

  1. // utils/performance.ts
  2. export class PerformanceMonitor {
  3. private marks: Map<string, number> = new Map();
  4. startMark(name: string) {
  5. this.marks.set(name, performance.now());
  6. performance.mark(`${name}-start`);
  7. }
  8. endMark(name: string) {
  9. const startTime = this.marks.get(name);
  10. if (startTime) {
  11. const duration = performance.now() - startTime;
  12. performance.mark(`${name}-end`);
  13. performance.measure(name, `${name}-start`, `${name}-end`);
  14. return {
  15. name,
  16. duration,
  17. timestamp: new Date().toISOString()
  18. };
  19. }
  20. }
  21. getMeasurements() {
  22. return performance.getEntriesByType('measure');
  23. }
  24. clearMeasurements() {
  25. performance.clearMarks();
  26. performance.clearMeasures();
  27. this.marks.clear();
  28. }
  29. }
  30. // 使用示例
  31. const monitor = new PerformanceMonitor();
  32. monitor.startMark('componentRender');
  33. // 组件渲染
  34. monitor.endMark('componentRender');

样式性能分析

样式加载监控

  1. // utils/styleMonitor.ts
  2. export const monitorStyleLoading = () => {
  3. const observer = new PerformanceObserver((list) => {
  4. list.getEntries().forEach((entry) => {
  5. if (entry.entryType === 'resource' && entry.name.endsWith('.css')) {
  6. console.log('CSS Performance:', {
  7. name: entry.name,
  8. duration: entry.duration,
  9. startTime: entry.startTime,
  10. transferSize: entry.transferSize
  11. });
  12. }
  13. });
  14. });
  15. observer.observe({ entryTypes: ['resource'] });
  16. };
  17. // 监控样式变化
  18. const styleChangeObserver = new MutationObserver((mutations) => {
  19. mutations.forEach((mutation) => {
  20. if (mutation.attributeName === 'class') {
  21. console.log('Style Change:', {
  22. element: mutation.target,
  23. oldValue: mutation.oldValue,
  24. newValue: (mutation.target as HTMLElement).className
  25. });
  26. }
  27. });
  28. });
  29. // 开始监控
  30. styleChangeObserver.observe(document.body, {
  31. attributes: true,
  32. attributeOldValue: true,
  33. attributeFilter: ['class'],
  34. subtree: true
  35. });

样式分析工具

  1. // utils/styleAnalyzer.ts
  2. export class StyleAnalyzer {
  3. analyzeDuplicateClasses() {
  4. const elements = document.querySelectorAll('*');
  5. const classCount = new Map<string, number>();
  6. elements.forEach((element) => {
  7. const classes = element.className.split(' ');
  8. classes.forEach((className) => {
  9. classCount.set(
  10. className,
  11. (classCount.get(className) || 0) + 1
  12. );
  13. });
  14. });
  15. return Array.from(classCount.entries())
  16. .filter(([_, count]) => count > 1)
  17. .sort(([_, a], [__, b]) => b - a);
  18. }
  19. analyzeUnusedStyles() {
  20. const styleSheets = Array.from(document.styleSheets);
  21. const usedSelectors = new Set();
  22. // 收集使用的选择器
  23. styleSheets.forEach((sheet) => {
  24. try {
  25. const rules = Array.from(sheet.cssRules);
  26. rules.forEach((rule) => {
  27. if (rule instanceof CSSStyleRule) {
  28. const elements = document.querySelectorAll(rule.selectorText);
  29. if (elements.length > 0) {
  30. usedSelectors.add(rule.selectorText);
  31. }
  32. }
  33. });
  34. } catch (e) {
  35. console.warn('Cannot read cssRules from stylesheet', e);
  36. }
  37. });
  38. return usedSelectors;
  39. }
  40. }

优化策略

样式优化

  1. // utils/styleOptimizer.ts
  2. export class StyleOptimizer {
  3. // 合并相似的类名
  4. optimizeClassNames(element: HTMLElement) {
  5. const classes = element.className.split(' ');
  6. const optimized = new Set<string>();
  7. // 处理边距和内边距
  8. const spacingClasses = {
  9. margin: [] as string[],
  10. padding: [] as string[]
  11. };
  12. classes.forEach((cls) => {
  13. if (cls.startsWith('m-')) {
  14. spacingClasses.margin.push(cls);
  15. } else if (cls.startsWith('p-')) {
  16. spacingClasses.padding.push(cls);
  17. } else {
  18. optimized.add(cls);
  19. }
  20. });
  21. // 合并边距类
  22. if (spacingClasses.margin.length > 0) {
  23. optimized.add(this.mergeSpacingClasses(spacingClasses.margin));
  24. }
  25. // 合并内边距类
  26. if (spacingClasses.padding.length > 0) {
  27. optimized.add(this.mergeSpacingClasses(spacingClasses.padding));
  28. }
  29. return Array.from(optimized).join(' ');
  30. }
  31. private mergeSpacingClasses(classes: string[]): string {
  32. // 实现边距类合并逻辑
  33. return classes[classes.length - 1];
  34. }
  35. }

运行时优化

  1. // utils/runtimeOptimizer.ts
  2. export class RuntimeOptimizer {
  3. private styleCache = new Map<string, HTMLStyleElement>();
  4. // 缓存动态生成的样式
  5. cacheStyles(className: string, styles: string) {
  6. if (!this.styleCache.has(className)) {
  7. const styleElement = document.createElement('style');
  8. styleElement.textContent = styles;
  9. document.head.appendChild(styleElement);
  10. this.styleCache.set(className, styleElement);
  11. }
  12. return className;
  13. }
  14. // 清理未使用的样式
  15. cleanupUnusedStyles() {
  16. const usedClasses = new Set<string>();
  17. // 收集当前使用的类名
  18. document.querySelectorAll('*').forEach((element) => {
  19. element.className.split(' ').forEach((cls) => {
  20. usedClasses.add(cls);
  21. });
  22. });
  23. // 清理未使用的缓存样式
  24. this.styleCache.forEach((style, className) => {
  25. if (!usedClasses.has(className)) {
  26. style.remove();
  27. this.styleCache.delete(className);
  28. }
  29. });
  30. }
  31. }

性能报告

报告生成器

  1. // utils/reportGenerator.ts
  2. export class PerformanceReportGenerator {
  3. generateReport() {
  4. const report = {
  5. timestamp: new Date().toISOString(),
  6. webVitals: this.collectWebVitals(),
  7. resourceMetrics: this.collectResourceMetrics(),
  8. styleMetrics: this.collectStyleMetrics(),
  9. recommendations: this.generateRecommendations()
  10. };
  11. return report;
  12. }
  13. private collectWebVitals() {
  14. // 收集 Web Vitals 指标
  15. }
  16. private collectResourceMetrics() {
  17. // 收集资源加载指标
  18. return performance.getEntriesByType('resource')
  19. .filter(entry => entry.name.endsWith('.css'))
  20. .map(entry => ({
  21. name: entry.name,
  22. duration: entry.duration,
  23. size: entry.transferSize
  24. }));
  25. }
  26. private collectStyleMetrics() {
  27. // 收集样式相关指标
  28. const styleSheets = document.styleSheets;
  29. return {
  30. styleSheetCount: styleSheets.length,
  31. totalRules: Array.from(styleSheets).reduce((count, sheet) => {
  32. try {
  33. return count + sheet.cssRules.length;
  34. } catch (e) {
  35. return count;
  36. }
  37. }, 0)
  38. };
  39. }
  40. private generateRecommendations() {
  41. // 生成优化建议
  42. const recommendations = [];
  43. // 检查样式表数量
  44. if (document.styleSheets.length > 5) {
  45. recommendations.push({
  46. type: 'warning',
  47. message: '考虑合并样式表以减少请求数'
  48. });
  49. }
  50. // 检查大型样式规则
  51. Array.from(document.styleSheets).forEach((sheet) => {
  52. try {
  53. if (sheet.cssRules.length > 1000) {
  54. recommendations.push({
  55. type: 'warning',
  56. message: `样式表 ${sheet.href} 包含过多规则`
  57. });
  58. }
  59. } catch (e) {}
  60. });
  61. return recommendations;
  62. }
  63. }

监控面板

性能仪表盘

  1. // components/PerformanceDashboard.tsx
  2. interface PerformanceMetrics {
  3. cls: number;
  4. fid: number;
  5. lcp: number;
  6. ttfb: number;
  7. fcp: number;
  8. }
  9. const PerformanceDashboard: React.FC = () => {
  10. const [metrics, setMetrics] = useState<PerformanceMetrics>();
  11. useEffect(() => {
  12. getCLS((metric) => {
  13. setMetrics(prev => ({ ...prev, cls: metric.value }));
  14. });
  15. getFID((metric) => {
  16. setMetrics(prev => ({ ...prev, fid: metric.value }));
  17. });
  18. getLCP((metric) => {
  19. setMetrics(prev => ({ ...prev, lcp: metric.value }));
  20. });
  21. getTTFB((metric) => {
  22. setMetrics(prev => ({ ...prev, ttfb: metric.value }));
  23. });
  24. getFCP((metric) => {
  25. setMetrics(prev => ({ ...prev, fcp: metric.value }));
  26. });
  27. }, []);
  28. return (
  29. <div className="grid grid-cols-3 gap-4 p-4">
  30. {metrics && Object.entries(metrics).map(([key, value]) => (
  31. <div key={key} className="p-4 bg-white rounded-lg shadow">
  32. <h3 className="text-lg font-semibold">{key.toUpperCase()}</h3>
  33. <p className="text-2xl">{value.toFixed(2)}</p>
  34. </div>
  35. ))}
  36. </div>
  37. );
  38. };

最佳实践

  1. 监控策略

    • 持续监控关键指标
    • 设置性能预警
    • 定期生成报告
  2. 优化建议

    • 定期清理未使用样式
    • 合并重复样式类
    • 优化样式加载顺序
  3. 开发建议

    • 使用性能分析工具
    • 遵循优化建议
    • 持续改进监控系统
  4. 维护策略

    • 定期检查性能报告
    • 更新优化策略
    • 响应性能预警