性能监控和优化是确保 Tailwind CSS 项目高效运行的关键。本节将详细介绍如何实施性能监控和进行相应的优化。
性能指标监控
Web Vitals 监控
// utils/vitals.ts
import { getCLS, getFID, getLCP, getTTFB, getFCP } from 'web-vitals';
const reportWebVitals = (onPerfEntry: (metric: any) => void) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
getCLS(onPerfEntry); // Cumulative Layout Shift
getFID(onPerfEntry); // First Input Delay
getLCP(onPerfEntry); // Largest Contentful Paint
getTTFB(onPerfEntry); // Time to First Byte
getFCP(onPerfEntry); // First Contentful Paint
}
};
export const setupVitalsReporting = () => {
reportWebVitals((metric) => {
// 发送到分析服务
console.log(metric);
// 可以发送到 Google Analytics
const analytics = (window as any).ga;
if (analytics) {
analytics('send', 'event', {
eventCategory: 'Web Vitals',
eventAction: metric.name,
eventValue: Math.round(metric.value),
eventLabel: metric.id,
nonInteraction: true,
});
}
});
};
自定义性能监控
// utils/performance.ts
export class PerformanceMonitor {
private marks: Map<string, number> = new Map();
startMark(name: string) {
this.marks.set(name, performance.now());
performance.mark(`${name}-start`);
}
endMark(name: string) {
const startTime = this.marks.get(name);
if (startTime) {
const duration = performance.now() - startTime;
performance.mark(`${name}-end`);
performance.measure(name, `${name}-start`, `${name}-end`);
return {
name,
duration,
timestamp: new Date().toISOString()
};
}
}
getMeasurements() {
return performance.getEntriesByType('measure');
}
clearMeasurements() {
performance.clearMarks();
performance.clearMeasures();
this.marks.clear();
}
}
// 使用示例
const monitor = new PerformanceMonitor();
monitor.startMark('componentRender');
// 组件渲染
monitor.endMark('componentRender');
样式性能分析
样式加载监控
// utils/styleMonitor.ts
export const monitorStyleLoading = () => {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.entryType === 'resource' && entry.name.endsWith('.css')) {
console.log('CSS Performance:', {
name: entry.name,
duration: entry.duration,
startTime: entry.startTime,
transferSize: entry.transferSize
});
}
});
});
observer.observe({ entryTypes: ['resource'] });
};
// 监控样式变化
const styleChangeObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'class') {
console.log('Style Change:', {
element: mutation.target,
oldValue: mutation.oldValue,
newValue: (mutation.target as HTMLElement).className
});
}
});
});
// 开始监控
styleChangeObserver.observe(document.body, {
attributes: true,
attributeOldValue: true,
attributeFilter: ['class'],
subtree: true
});
样式分析工具
// utils/styleAnalyzer.ts
export class StyleAnalyzer {
analyzeDuplicateClasses() {
const elements = document.querySelectorAll('*');
const classCount = new Map<string, number>();
elements.forEach((element) => {
const classes = element.className.split(' ');
classes.forEach((className) => {
classCount.set(
className,
(classCount.get(className) || 0) + 1
);
});
});
return Array.from(classCount.entries())
.filter(([_, count]) => count > 1)
.sort(([_, a], [__, b]) => b - a);
}
analyzeUnusedStyles() {
const styleSheets = Array.from(document.styleSheets);
const usedSelectors = new Set();
// 收集使用的选择器
styleSheets.forEach((sheet) => {
try {
const rules = Array.from(sheet.cssRules);
rules.forEach((rule) => {
if (rule instanceof CSSStyleRule) {
const elements = document.querySelectorAll(rule.selectorText);
if (elements.length > 0) {
usedSelectors.add(rule.selectorText);
}
}
});
} catch (e) {
console.warn('Cannot read cssRules from stylesheet', e);
}
});
return usedSelectors;
}
}
优化策略
样式优化
// utils/styleOptimizer.ts
export class StyleOptimizer {
// 合并相似的类名
optimizeClassNames(element: HTMLElement) {
const classes = element.className.split(' ');
const optimized = new Set<string>();
// 处理边距和内边距
const spacingClasses = {
margin: [] as string[],
padding: [] as string[]
};
classes.forEach((cls) => {
if (cls.startsWith('m-')) {
spacingClasses.margin.push(cls);
} else if (cls.startsWith('p-')) {
spacingClasses.padding.push(cls);
} else {
optimized.add(cls);
}
});
// 合并边距类
if (spacingClasses.margin.length > 0) {
optimized.add(this.mergeSpacingClasses(spacingClasses.margin));
}
// 合并内边距类
if (spacingClasses.padding.length > 0) {
optimized.add(this.mergeSpacingClasses(spacingClasses.padding));
}
return Array.from(optimized).join(' ');
}
private mergeSpacingClasses(classes: string[]): string {
// 实现边距类合并逻辑
return classes[classes.length - 1];
}
}
运行时优化
// utils/runtimeOptimizer.ts
export class RuntimeOptimizer {
private styleCache = new Map<string, HTMLStyleElement>();
// 缓存动态生成的样式
cacheStyles(className: string, styles: string) {
if (!this.styleCache.has(className)) {
const styleElement = document.createElement('style');
styleElement.textContent = styles;
document.head.appendChild(styleElement);
this.styleCache.set(className, styleElement);
}
return className;
}
// 清理未使用的样式
cleanupUnusedStyles() {
const usedClasses = new Set<string>();
// 收集当前使用的类名
document.querySelectorAll('*').forEach((element) => {
element.className.split(' ').forEach((cls) => {
usedClasses.add(cls);
});
});
// 清理未使用的缓存样式
this.styleCache.forEach((style, className) => {
if (!usedClasses.has(className)) {
style.remove();
this.styleCache.delete(className);
}
});
}
}
性能报告
报告生成器
// utils/reportGenerator.ts
export class PerformanceReportGenerator {
generateReport() {
const report = {
timestamp: new Date().toISOString(),
webVitals: this.collectWebVitals(),
resourceMetrics: this.collectResourceMetrics(),
styleMetrics: this.collectStyleMetrics(),
recommendations: this.generateRecommendations()
};
return report;
}
private collectWebVitals() {
// 收集 Web Vitals 指标
}
private collectResourceMetrics() {
// 收集资源加载指标
return performance.getEntriesByType('resource')
.filter(entry => entry.name.endsWith('.css'))
.map(entry => ({
name: entry.name,
duration: entry.duration,
size: entry.transferSize
}));
}
private collectStyleMetrics() {
// 收集样式相关指标
const styleSheets = document.styleSheets;
return {
styleSheetCount: styleSheets.length,
totalRules: Array.from(styleSheets).reduce((count, sheet) => {
try {
return count + sheet.cssRules.length;
} catch (e) {
return count;
}
}, 0)
};
}
private generateRecommendations() {
// 生成优化建议
const recommendations = [];
// 检查样式表数量
if (document.styleSheets.length > 5) {
recommendations.push({
type: 'warning',
message: '考虑合并样式表以减少请求数'
});
}
// 检查大型样式规则
Array.from(document.styleSheets).forEach((sheet) => {
try {
if (sheet.cssRules.length > 1000) {
recommendations.push({
type: 'warning',
message: `样式表 ${sheet.href} 包含过多规则`
});
}
} catch (e) {}
});
return recommendations;
}
}
监控面板
性能仪表盘
// components/PerformanceDashboard.tsx
interface PerformanceMetrics {
cls: number;
fid: number;
lcp: number;
ttfb: number;
fcp: number;
}
const PerformanceDashboard: React.FC = () => {
const [metrics, setMetrics] = useState<PerformanceMetrics>();
useEffect(() => {
getCLS((metric) => {
setMetrics(prev => ({ ...prev, cls: metric.value }));
});
getFID((metric) => {
setMetrics(prev => ({ ...prev, fid: metric.value }));
});
getLCP((metric) => {
setMetrics(prev => ({ ...prev, lcp: metric.value }));
});
getTTFB((metric) => {
setMetrics(prev => ({ ...prev, ttfb: metric.value }));
});
getFCP((metric) => {
setMetrics(prev => ({ ...prev, fcp: metric.value }));
});
}, []);
return (
<div className="grid grid-cols-3 gap-4 p-4">
{metrics && Object.entries(metrics).map(([key, value]) => (
<div key={key} className="p-4 bg-white rounded-lg shadow">
<h3 className="text-lg font-semibold">{key.toUpperCase()}</h3>
<p className="text-2xl">{value.toFixed(2)}</p>
</div>
))}
</div>
);
};
最佳实践
监控策略
- 持续监控关键指标
- 设置性能预警
- 定期生成报告
优化建议
- 定期清理未使用样式
- 合并重复样式类
- 优化样式加载顺序
开发建议
- 使用性能分析工具
- 遵循优化建议
- 持续改进监控系统
维护策略
- 定期检查性能报告
- 更新优化策略
- 响应性能预警