前端性能优化实战:从指标监控到极致体验
引言
性能优化不是简单的"加个缓存"或"压缩一下图片",而是一个系统性的工程。当你的应用面临百万级用户、复杂业务逻辑、多变网络环境时,每一个毫秒的优化都可能带来显著的业务提升。
本文将从五个维度展开:
- 性能指标体系:从RAIL到Core Web Vitals
- 渲染性能优化:从浏览器渲染原理到实战优化
- 网络性能优化:从请求优化到资源加载策略
- 构建性能优化:从打包策略到产物优化
- 运行时性能优化:从内存管理到长任务调度
一、性能指标体系:从RAIL到Core Web Vitals
1.1 核心性能指标
typescript
// performance/metrics/CoreWebVitals.ts - 核心Web指标采集
export class CoreWebVitals {
private static instance: CoreWebVitals;
private metrics: Map<string, MetricValue> = new Map();
private observers: Array<(metric: MetricValue) => void> = [];
private constructor() {
this.initializeObservers();
}
static getInstance(): CoreWebVitals {
if (!this.instance) {
this.instance = new CoreWebVitals();
}
return this.instance;
}
// 初始化所有观察器
private initializeObservers(): void {
// LCP - 最大内容绘制
this.observeLCP();
// FID - 首次输入延迟
this.observeFID();
// CLS - 累积布局偏移
this.observeCLS();
// INP - 交互到下一次绘制
this.observeINP();
// TTFB - 首字节时间
this.observeTTFB();
// FCP - 首次内容绘制
this.observeFCP();
}
// 观察LCP
private observeLCP(): void {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1] as LargestContentfulPaint;
if (lastEntry) {
this.recordMetric({
name: 'LCP',
value: lastEntry.startTime,
rating: this.getRating('LCP', lastEntry.startTime),
timestamp: performance.now()
});
}
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
}
// 观察FID
private observeFID(): void {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const firstEntry = entries[0] as PerformanceEventTiming;
if (firstEntry) {
this.recordMetric({
name: 'FID',
value: firstEntry.processingStart - firstEntry.startTime,
rating: this.getRating('FID', firstEntry.processingStart - firstEntry.startTime),
timestamp: performance.now()
});
}
});
observer.observe({ type: 'first-input', buffered: true });
}
// 观察CLS
private observeCLS(): void {
let clsValue = 0;
let clsEntries: LayoutShift[] = [];
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries() as LayoutShift[];
entries.forEach((entry) => {
// 忽略没有最近用户输入的布局偏移
if (!entry.hadRecentInput) {
clsValue += entry.value;
clsEntries.push(entry);
}
});
this.recordMetric({
name: 'CLS',
value: clsValue,
rating: this.getRating('CLS', clsValue),
timestamp: performance.now()
});
});
observer.observe({ type: 'layout-shift', buffered: true });
}
// 观察INP
private observeINP(): void {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries() as PerformanceEventTiming[];
entries.forEach((entry) => {
// 只关注交互事件
if (entry.interactionId > 0) {
const duration = entry.duration;
this.recordMetric({
name: 'INP',
value: duration,
rating: this.getRating('INP', duration),
timestamp: performance.now()
});
}
});
});
observer.observe({ type: 'event', buffered: true });
}
// 观察TTFB
private observeTTFB(): void {
const navigationEntry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
if (navigationEntry) {
const ttfb = navigationEntry.responseStart - navigationEntry.requestStart;
this.recordMetric({
name: 'TTFB',
value: ttfb,
rating: this.getRating('TTFB', ttfb),
timestamp: performance.now()
});
}
}
// 观察FCP
private observeFCP(): void {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const firstEntry = entries[0];
if (firstEntry) {
this.recordMetric({
name: 'FCP',
value: firstEntry.startTime,
rating: this.getRating('FCP', firstEntry.startTime),
timestamp: performance.now()
});
}
});
observer.observe({ type: 'paint', buffered: true });
}
// 获取评分
private getRating(metricName: string, value: number): 'good' | 'needs-improvement' | 'poor' {
const thresholds: Record<string, { good: number; poor: number }> = {
'LCP': { good: 2500, poor: 4000 },
'FID': { good: 100, poor: 300 },
'CLS': { good: 0.1, poor: 0.25 },
'INP': { good: 200, poor: 500 },
'TTFB': { good: 800, poor: 1800 },
'FCP': { good: 1800, poor: 3000 }
};
const threshold = thresholds[metricName];
if (!threshold) return 'needs-improvement';
if (value <= threshold.good) return 'good';
if (value <= threshold.poor) return 'needs-improvement';
return 'poor';
}
// 记录指标
private recordMetric(metric: MetricValue): void {
this.metrics.set(metric.name, metric);
// 通知所有订阅者
this.observers.forEach(observer => observer(metric));
// 发送到监控平台
this.sendToAnalytics(metric);
}
// 订阅指标变化
subscribe(callback: (metric: MetricValue) => void): () => void {
this.observers.push(callback);
return () => {
this.observers = this.observers.filter(obs => obs !== callback);
};
}
// 获取所有指标
getAllMetrics(): Map<string, MetricValue> {
return this.metrics;
}
// 发送到监控平台
private sendToAnalytics(metric: MetricValue): void {
// 使用sendBeacon确保数据发送
if (navigator.sendBeacon) {
const data = JSON.stringify({
...metric,
page: window.location.pathname,
timestamp: Date.now()
});
navigator.sendBeacon('/api/metrics', data);
} else {
// 降级方案:使用fetch
fetch('/api/metrics', {
method: 'POST',
body: JSON.stringify(metric),
keepalive: true
});
}
}
}
// 使用示例
const webVitals = CoreWebVitals.getInstance();
webVitals.subscribe((metric) => {
console.log(`[${metric.name}] ${metric.value}ms - ${metric.rating}`);
});
// 在React中使用
// performance/PerformanceMonitor.tsx
export const PerformanceMonitor: React.FC = ({ children }) => {
useEffect(() => {
const webVitals = CoreWebVitals.getInstance();
// 上报指标
webVitals.subscribe((metric) => {
// 发送到监控平台
reportWebVital(metric);
});
// 监听用户交互
const handleUserInteraction = () => {
// 标记用户交互时间
performance.mark('user-interaction');
};
window.addEventListener('click', handleUserInteraction);
window.addEventListener('keydown', handleUserInteraction);
return () => {
window.removeEventListener('click', handleUserInteraction);
window.removeEventListener('keydown', handleUserInteraction);
};
}, []);
return children;
};
1.2 自定义性能指标
typescript
// performance/metrics/CustomMetrics.ts - 自定义业务指标
export class CustomMetrics {
private static instance: CustomMetrics;
private metrics: Map<string, CustomMetricValue> = new Map();
private constructor() {}
static getInstance(): CustomMetrics {
if (!this.instance) {
this.instance = new CustomMetrics();
}
return this.instance;
}
// 记录组件渲染时间
measureComponentRender(componentName: string, renderTime: number): void {
this.recordMetric({
name: `render:${componentName}`,
value: renderTime,
type: 'timing',
timestamp: Date.now()
});
}
// 记录API请求时间
measureApiCall(apiName: string, duration: number, status: 'success' | 'error'): void {
this.recordMetric({
name: `api:${apiName}:${status}`,
value: duration,
type: 'timing',
timestamp: Date.now()
});
}
// 记录交互响应时间
measureInteraction(interactionName: string, duration: number): void {
this.recordMetric({
name: `interaction:${interactionName}`,
value: duration,
type: 'timing',
timestamp: Date.now()
});
}
// 记录资源加载时间
measureResourceLoad(resourceType: string, duration: number, size: number): void {
this.recordMetric({
name: `resource:${resourceType}`,
value: duration,
type: 'timing',
timestamp: Date.now(),
metadata: { size }
});
}
// 记录内存使用
measureMemoryUsage(): void {
if (performance.memory) {
const memory = performance.memory as PerformanceMemory;
this.recordMetric({
name: 'memory:usedJSHeapSize',
value: memory.usedJSHeapSize,
type: 'gauge',
timestamp: Date.now()
});
}
}
// 记录帧率
measureFrameRate(): void {
let frameCount = 0;
let lastTime = performance.now();
const checkFrameRate = () => {
frameCount++;
const currentTime = performance.now();
if (currentTime - lastTime >= 1000) {
const fps = frameCount * 1000 / (currentTime - lastTime);
this.recordMetric({
name: 'fps',
value: fps,
type: 'gauge',
timestamp: Date.now()
});
frameCount = 0;
lastTime = currentTime;
}
requestAnimationFrame(checkFrameRate);
};
requestAnimationFrame(checkFrameRate);
}
// 记录自定义指标
private recordMetric(metric: CustomMetricValue): void {
this.metrics.set(metric.name, metric);
// 批量上报
this.batchReport();
}
// 批量上报指标
private batchReport(): void {
// 使用requestIdleCallback批量发送
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
this.sendMetrics();
});
} else {
setTimeout(() => this.sendMetrics(), 1000);
}
}
// 发送指标
private sendMetrics(): void {
const metrics = Array.from(this.metrics.values());
// 使用sendBeacon发送
if (navigator.sendBeacon && metrics.length > 0) {
navigator.sendBeacon('/api/custom-metrics', JSON.stringify(metrics));
this.metrics.clear();
}
}
}
// 使用示例
const customMetrics = CustomMetrics.getInstance();
// 在React组件中使用
export const withPerformanceTracking = <P extends object>(
WrappedComponent: React.ComponentType<P>,
componentName: string
): React.FC<P> => {
return (props: P) => {
const startTime = useRef(performance.now());
useEffect(() => {
const renderTime = performance.now() - startTime.current;
customMetrics.measureComponentRender(componentName, renderTime);
});
return <WrappedComponent {...props} />;
};
};
// 在API调用中使用
export const trackApiCall = async <T>(
apiName: string,
apiCall: () => Promise<T>
): Promise<T> => {
const startTime = performance.now();
try {
const result = await apiCall();
const duration = performance.now() - startTime;
customMetrics.measureApiCall(apiName, duration, 'success');
return result;
} catch (error) {
const duration = performance.now() - startTime;
customMetrics.measureApiCall(apiName, duration, 'error');
throw error;
}
};
二、渲染性能优化:从浏览器渲染原理到实战优化
2.1 渲染管线优化
ini
// performance/rendering/RenderPipelineOptimizer.ts - 渲染管线优化
export class RenderPipelineOptimizer {
private static instance: RenderPipelineOptimizer;
private frameCallbacks: Array<() => void> = [];
private isScheduled = false;
private constructor() {
this.initializeFrameLoop();
}
static getInstance(): RenderPipelineOptimizer {
if (!this.instance) {
this.instance = new RenderPipelineOptimizer();
}
return this.instance;
}
// 初始化帧循环
private initializeFrameLoop(): void {
const loop = () => {
// 执行所有帧回调
const callbacks = this.frameCallbacks;
this.frameCallbacks = [];
this.isScheduled = false;
callbacks.forEach(callback => callback());
// 继续下一帧
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
}
// 在下一帧执行
nextFrame(callback: () => void): void {
this.frameCallbacks.push(callback);
if (!this.isScheduled) {
this.isScheduled = true;
requestAnimationFrame(() => {
this.isScheduled = false;
const callbacks = this.frameCallbacks;
this.frameCallbacks = [];
callbacks.forEach(cb => cb());
});
}
}
// 批量DOM更新
batchDOMUpdate(updates: Array<() => void>): void {
this.nextFrame(() => {
// 使用documentFragment避免多次重排
const fragment = document.createDocumentFragment();
updates.forEach(update => {
// 在Fragment中执行更新
update();
});
// 一次性插入
document.body.appendChild(fragment);
});
}
// 防抖渲染
debounceRender<T>(
fn: (args: T) => void,
delay: number = 16
): (args: T) => void {
let timeout: number | null = null;
let lastArgs: T | null = null;
return (args: T) => {
lastArgs = args;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
if (lastArgs) {
fn(lastArgs);
}
}, delay);
};
}
// 节流渲染
throttleRender<T>(
fn: (args: T) => void,
limit: number = 16
): (args: T) => void {
let inThrottle = false;
let lastArgs: T | null = null;
return (args: T) => {
lastArgs = args;
if (!inThrottle) {
inThrottle = true;
fn(args);
setTimeout(() => {
inThrottle = false;
if (lastArgs) {
fn(lastArgs);
}
}, limit);
}
};
}
}
// 使用示例
const renderOptimizer = RenderPipelineOptimizer.getInstance();
// 批量更新列表
const updateList = (items: HTMLElement[]) => {
renderOptimizer.batchDOMUpdate(
items.map(item => () => {
// 更新每个item
})
);
};
// 防抖搜索
const debouncedSearch = renderOptimizer.debounceRender(
(query: string) => {
// 执行搜索
},
300
);
2.2 虚拟滚动优化
ini
// performance/rendering/VirtualList.tsx - 虚拟滚动列表
interface VirtualListProps<T> {
items: T[];
itemHeight: number;
containerHeight: number;
overscan?: number;
renderItem: (item: T, index: number) => React.ReactNode;
onEndReached?: () => void;
}
export const VirtualList = <T,>({
items,
itemHeight,
containerHeight,
overscan = 3,
renderItem,
onEndReached
}: VirtualListProps<T>): React.ReactElement => {
const [scrollTop, setScrollTop] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);
const scrollTimeout = useRef<number>();
// 计算可见范围
const visibleCount = Math.ceil(containerHeight / itemHeight);
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
const endIndex = Math.min(
items.length,
Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan
);
// 可见项
const visibleItems = items.slice(startIndex, endIndex);
// 处理滚动
const handleScroll = useCallback(() => {
if (scrollTimeout.current) {
requestAnimationFrame(() => {
const container = containerRef.current;
if (container) {
setScrollTop(container.scrollTop);
// 检查是否到达底部
if (
container.scrollHeight - container.scrollTop - container.clientHeight <
itemHeight * 2
) {
onEndReached?.();
}
}
});
}
}, [itemHeight, onEndReached]);
// 使用rAF优化滚动
const throttledScroll = useMemo(() => {
let ticking = false;
return () => {
if (!ticking) {
requestAnimationFrame(() => {
handleScroll();
ticking = false;
});
ticking = true;
}
};
}, [handleScroll]);
return (
<div
ref={containerRef}
onScroll={throttledScroll}
style={{
height: containerHeight,
overflowY: 'auto',
position: 'relative'
}}
>
{/* 占位div,用于生成滚动条 */}
<div
style={{
height: items.length * itemHeight,
position: 'relative'
}}
>
{/* 可见项 */}
{visibleItems.map((item, index) => (
<div
key={index}
style={{
position: 'absolute',
top: (startIndex + index) * itemHeight,
height: itemHeight,
width: '100%'
}}
>
{renderItem(item, startIndex + index)}
</div>
))}
</div>
</div>
);
};
2.3 组件级渲染优化
ini
// performance/rendering/ComponentOptimizer.tsx - 组件渲染优化
import React, { memo, useMemo, useCallback } from 'react';
// 1. 使用React.memo避免不必要的重渲染
interface ExpensiveComponentProps {
data: ComplexData;
onUpdate: (id: string) => void;
}
export const ExpensiveComponent = memo(function ExpensiveComponent({
data,
onUpdate
}: ExpensiveComponentProps) {
// 复杂计算
const processedData = useMemo(() => {
return processComplexData(data);
}, [data]);
// 使用useCallback避免子组件重渲染
const handleUpdate = useCallback(() => {
onUpdate(data.id);
}, [data.id, onUpdate]);
return (
<div>
{/* 渲染内容 */}
</div>
);
});
// 2. 使用React.lazy和Suspense实现代码分割
const LazyComponent = React.lazy(() => import('./LazyComponent'));
// 3. 使用useDeferredValue处理低优先级更新
export const SearchComponent: React.FC = () => {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const searchResults = useMemo(() => {
return searchData(deferredQuery);
}, [deferredQuery]);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="搜索..."
/>
{/* 使用deferredQuery渲染结果 */}
<SearchResults results={searchResults} />
</div>
);
};
// 4. 使用useTransition处理非紧急更新
export const TransitionComponent: React.FC = () => {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('home');
const handleTabChange = (newTab: string) => {
startTransition(() => {
setTab(newTab);
});
};
return (
<div>
<TabBar currentTab={tab} onChange={handleTabChange} />
{isPending ? <Spinner /> : <TabContent tab={tab} />}
</div>
);
};
// 5. 使用useMemo优化复杂计算
export const DataGrid: React.FC<{ data: DataItem[] }> = ({ data }) => {
const sortedData = useMemo(() => {
return [...data].sort((a, b) => a.value - b.value);
}, [data]);
const filteredData = useMemo(() => {
return sortedData.filter(item => item.value > 0);
}, [sortedData]);
const total = useMemo(() => {
return filteredData.reduce((sum, item) => sum + item.value, 0);
}, [filteredData]);
return (
<div>
<GridHeader total={total} />
<GridBody data={filteredData} />
</div>
);
};
三、网络性能优化:从请求优化到资源加载策略
3.1 请求优化策略
typescript
// performance/network/RequestOptimizer.ts - 请求优化器
export class RequestOptimizer {
private static instance: RequestOptimizer;
private cache: Map<string, CachedResponse> = new Map();
private inflightRequests: Map<string, Promise<any>> = new Map();
private requestQueue: Array<QueuedRequest> = [];
private constructor() {}
static getInstance(): RequestOptimizer {
if (!this.instance) {
this.instance = new RequestOptimizer();
}
return this.instance;
}
// 智能请求(带缓存、去重、优先级)
async smartRequest<T>(config: RequestConfig): Promise<T> {
const cacheKey = this.generateCacheKey(config);
// 1. 检查内存缓存
const cached = this.cache.get(cacheKey);
if (cached && !this.isExpired(cached)) {
return cached.data as T;
}
// 2. 检查是否有相同的请求正在进行
const inflight = this.inflightRequests.get(cacheKey);
if (inflight) {
return inflight;
}
// 3. 创建新请求
const requestPromise = this.performRequest<T>(config, cacheKey);
this.inflightRequests.set(cacheKey, requestPromise);
try {
return await requestPromise;
} finally {
this.inflightRequests.delete(cacheKey);
}
}
// 批量请求合并
async batchRequest<T>(
requests: RequestConfig[],
options?: BatchOptions
): Promise<T[]> {
// 按优先级排序
const sortedRequests = [...requests].sort(
(a, b) => (b.priority || 0) - (a.priority || 0)
);
// 分批执行
const batchSize = options?.batchSize || 4;
const results: T[] = [];
for (let i = 0; i < sortedRequests.length; i += batchSize) {
const batch = sortedRequests.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(request => this.smartRequest<T>(request))
);
results.push(...batchResults);
}
return results;
}
// 请求重试
private async performRequest<T>(
config: RequestConfig,
cacheKey: string
): Promise<T> {
const maxRetries = config.retries || 3;
const baseDelay = config.retryDelay || 1000;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await this.executeRequest<T>(config);
// 缓存响应
if (config.cacheable !== false) {
this.cache.set(cacheKey, {
data: response,
timestamp: Date.now(),
maxAge: config.cacheMaxAge || 5 * 60 * 1000
});
}
return response;
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
// 指数退避
const delay = baseDelay * Math.pow(2, attempt);
await this.sleep(delay);
}
}
throw new Error('Request failed');
}
// 执行请求
private async executeRequest<T>(config: RequestConfig): Promise<T> {
const { url, method = 'GET', params, data, headers, timeout } = config;
// 构建完整URL
const fullUrl = this.buildUrl(url, params);
// 创建AbortController用于超时
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout || 10000);
try {
const response = await fetch(fullUrl, {
method,
headers: {
'Content-Type': 'application/json',
...headers
},
body: data ? JSON.stringify(data) : undefined,
signal: controller.signal
});
if (!response.ok) {
throw new HttpError(response.status, response.statusText);
}
return await response.json();
} finally {
clearTimeout(timeoutId);
}
}
// 生成缓存键
private generateCacheKey(config: RequestConfig): string {
return `${config.method}:${config.url}:${JSON.stringify(config.params || {})}`;
}
// 检查缓存是否过期
private isExpired(cached: CachedResponse): boolean {
return Date.now() - cached.timestamp > cached.maxAge;
}
// 构建URL
private buildUrl(url: string, params?: Record<string, unknown>): string {
if (!params) return url;
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
searchParams.append(key, String(value));
}
});
const separator = url.includes('?') ? '&' : '?';
return `${url}${separator}${searchParams.toString()}`;
}
// 睡眠
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// 使用示例
const requestOptimizer = RequestOptimizer.getInstance();
// 智能请求
const userData = await requestOptimizer.smartRequest<User>({
url: '/api/users/123',
method: 'GET',
cacheable: true,
cacheMaxAge: 5 * 60 * 1000,
priority: 10
});
// 批量请求
const [user, posts, comments] = await requestOptimizer.batchRequest([
{ url: '/api/users/123', method: 'GET' },
{ url: '/api/users/123/posts', method: 'GET' },
{ url: '/api/posts/456/comments', method: 'GET' }
], {
batchSize: 2
});
3.2 资源加载策略
ini
// performance/network/ResourceLoadStrategy.ts - 资源加载策略
export class ResourceLoadStrategy {
private static instance: ResourceLoadStrategy;
private loadedResources: Set<string> = new Set();
private preloadQueue: Array<Resource> = [];
private constructor() {
this.initializePrefetch();
}
static getInstance(): ResourceLoadStrategy {
if (!this.instance) {
this.instance = new ResourceLoadStrategy();
}
return this.instance;
}
// 初始化预取
private initializePrefetch(): void {
// 页面空闲时预取
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
this.processPreloadQueue();
});
}
}
// 预加载关键资源
preloadCritical(resources: Resource[]): void {
resources.forEach(resource => {
this.preloadQueue.push(resource);
});
}
// 预连接
preconnect(origins: string[]): void {
origins.forEach(origin => {
const link = document.createElement('link');
link.rel = 'preconnect';
link.href = origin;
document.head.appendChild(link);
});
}
// DNS预解析
dnsPrefetch(hosts: string[]): void {
hosts.forEach(host => {
const link = document.createElement('link');
link.rel = 'dns-prefetch';
link.href = host;
document.head.appendChild(link);
});
}
// 预渲染
prerender(urls: string[]): void {
urls.forEach(url => {
const link = document.createElement('link');
link.rel = 'prerender';
link.href = url;
document.head.appendChild(link);
});
}
// 处理预加载队列
private processPreloadQueue(): void {
// 按优先级排序
const sorted = this.preloadQueue.sort(
(a, b) => (b.priority || 0) - (a.priority || 0)
);
// 限制并发数
const concurrency = 3;
const chunks = this.chunkArray(sorted, concurrency);
chunks.forEach(chunk => {
Promise.all(chunk.map(resource => this.loadResource(resource)));
});
this.preloadQueue = [];
}
// 加载资源
private async loadResource(resource: Resource): Promise<void> {
if (this.loadedResources.has(resource.url)) {
return;
}
this.loadedResources.add(resource.url);
switch (resource.type) {
case 'script':
await this.loadScript(resource);
break;
case 'style':
await this.loadStyle(resource);
break;
case 'image':
await this.loadImage(resource);
break;
case 'font':
await this.loadFont(resource);
break;
}
}
// 加载脚本
private loadScript(resource: Resource): Promise<void> {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = resource.url;
script.async = true;
if (resource.module) {
script.type = 'module';
}
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Failed to load: ${resource.url}`));
document.head.appendChild(script);
});
}
// 加载样式
private loadStyle(resource: Resource): Promise<void> {
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = resource.url;
link.onload = () => resolve();
link.onerror = () => reject(new Error(`Failed to load: ${resource.url}`));
document.head.appendChild(link);
});
}
// 加载图片
private loadImage(resource: Resource): Promise<void> {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = resource.url;
img.onload = () => resolve();
img.onerror = () => reject(new Error(`Failed to load: ${resource.url}`));
});
}
// 加载字体
private loadFont(resource: Resource): Promise<void> {
const font = new FontFace(resource.family, `url(${resource.url})`);
return font.load().then(() => {
document.fonts.add(font);
});
}
// 分块数组
private chunkArray<T>(array: T[], size: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
}
// 使用示例
const resourceStrategy = ResourceLoadStrategy.getInstance();
// 预连接
resourceStrategy.preconnect([
'https://api.example.com',
'https://cdn.example.com'
]);
// DNS预解析
resourceStrategy.dnsPrefetch([
'https://images.example.com',
'https://fonts.example.com'
]);
// 预加载关键资源
resourceStrategy.preloadCritical([
{
url: '/critical.js',
type: 'script',
priority: 10
},
{
url: '/styles.css',
type: 'style',
priority: 8
}
]);
四、构建性能优化:从打包策略到产物优化
4.1 构建配置优化
php
// vite.config.ts - Vite构建优化配置
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { visualizer } from 'rollup-plugin-visualizer';
import { terser } from 'rollup-plugin-terser';
export default defineConfig({
plugins: [react()],
build: {
// 目标浏览器
target: 'es2018',
// 输出目录
outDir: 'dist',
// 资源目录
assetsDir: 'assets',
// 生成sourcemap
sourcemap: true,
// 代码分割
rollupOptions: {
output: {
// 手动分包
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'router-vendor': ['react-router-dom'],
'query-vendor': ['@tanstack/react-query'],
'ui-vendor': ['antd', '@ant-design/icons'],
'utils-vendor': ['lodash', 'dayjs']
},
// 文件命名
entryFileNames: 'assets/js/[name]-[hash].js',
chunkFileNames: 'assets/js/[name]-[hash].js',
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
}
},
// 压缩配置
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log', 'console.info']
},
mangle: true,
format: {
comments: false
}
},
// 资源内联阈值
assetsInlineLimit: 4096, // 4KB以下的资源内联
// 构建报告
reportCompressedSize: true,
// 块大小警告
chunkSizeWarningLimit: 500
},
// 开发服务器优化
server: {
// 依赖预构建
optimizeDeps: {
include: ['react', 'react-dom', 'react-router-dom']
}
}
});
4.2 代码分割策略
typescript
// performance/build/CodeSplitting.tsx - 代码分割策略
import React, { Suspense, lazy } from 'react';
// 1. 路由级代码分割
const HomePage = lazy(() => import('@/pages/Home'));
const AboutPage = lazy(() => import('@/pages/About'));
const ContactPage = lazy(() => import('@/pages/Contact'));
// 2. 组件级代码分割
const HeavyComponent = lazy(() => import('@/components/HeavyComponent'));
const ChartComponent = lazy(() => import('@/components/Chart'));
// 3. 条件加载
const AdminPanel = lazy(() => import('@/components/AdminPanel'));
// 4. 按需加载第三方库
const loadEditor = () => import('@/lib/editor');
const loadPDFViewer = () => import('@/lib/pdf-viewer');
// 路由配置
export const routes = [
{
path: '/',
element: (
<Suspense fallback={<PageLoader />}>
<HomePage />
</Suspense>
)
},
{
path: '/about',
element: (
<Suspense fallback={<PageLoader />}>
<AboutPage />
</Suspense>
)
},
{
path: '/contact',
element: (
<Suspense fallback={<PageLoader />}>
<ContactPage />
</Suspense>
)
}
];
// 条件加载组件
export const ConditionalComponent: React.FC<{ isAdmin: boolean }> = ({
isAdmin
}) => {
const [showAdminPanel, setShowAdminPanel] = useState(false);
return (
<div>
<button onClick={() => setShowAdminPanel(true)}>
显示管理面板
</button>
{showAdminPanel && isAdmin && (
<Suspense fallback={<Loading />}>
<AdminPanel />
</Suspense>
)}
</div>
);
};
// 按需加载编辑器
export const EditorContainer: React.FC = () => {
const [editor, setEditor] = useState<any>(null);
const loadEditorAsync = async () => {
const module = await loadEditor();
setEditor(module.default);
};
useEffect(() => {
loadEditorAsync();
}, []);
return (
<div>
{editor ? (
<editor.Component />
) : (
<Loading message="加载编辑器..." />
)}
</div>
);
};
4.3 构建优化工具
csharp
// performance/build/BuildOptimizer.ts - 构建优化工具
export class BuildOptimizer {
// 分析包大小
static analyzeBundleSize(): void {
// 使用rollup-plugin-visualizer生成报告
// 在vite.config.ts中配置
}
// 优化图片资源
static optimizeImages(images: string[]): Promise<void> {
// 使用sharp进行图片压缩
// 使用webp/avif格式
return Promise.all(
images.map(async (image) => {
const sharp = require('sharp');
const buffer = await sharp(image)
.resize(800, 600, { fit: 'inside' })
.webp({ quality: 80 })
.toBuffer();
// 写入优化后的图片
await require('fs').promises.writeFile(
image.replace(/.(png|jpg)$/, '.webp'),
buffer
);
})
);
}
// 优化CSS
static optimizeCSS(): void {
// 使用PurgeCSS移除未使用的CSS
// 使用CSSNano压缩CSS
}
// 优化JavaScript
static optimizeJavaScript(): void {
// 使用Terser压缩
// 使用Tree Shaking移除未使用的代码
// 使用代码分割
}
// 优化字体
static optimizeFonts(fonts: string[]): void {
// 使用fontmin子集化字体
// 使用woff2格式
}
// 生成资源清单
static generateManifest(): void {
// 生成manifest.json
// 包含所有资源的hash和路径
}
}
五、运行时性能优化:从内存管理到长任务调度
5.1 内存管理优化
typescript
// performance/runtime/MemoryOptimizer.ts - 内存优化器
export class MemoryOptimizer {
private static instance: MemoryOptimizer;
private eventListeners: Array<{
target: EventTarget;
type: string;
listener: EventListener;
options?: AddEventListenerOptions;
}> = [];
private constructor() {}
static getInstance(): MemoryOptimizer {
if (!this.instance) {
this.instance = new MemoryOptimizer();
}
return this.instance;
}
// 注册事件监听器(自动追踪)
addEventListener(
target: EventTarget,
type: string,
listener: EventListener,
options?: AddEventListenerOptions
): void {
target.addEventListener(type, listener, options);
this.eventListeners.push({ target, type, listener, options });
}
// 移除所有事件监听器
removeAllEventListeners(): void {
this.eventListeners.forEach(({ target, type, listener, options }) => {
target.removeEventListener(type, listener, options);
});
this.eventListeners = [];
}
// 清理不再使用的对象
cleanup(): void {
// 1. 移除事件监听器
this.removeAllEventListeners();
// 2. 清除定时器
this.clearAllTimers();
// 3. 清除缓存
this.clearCaches();
// 4. 强制垃圾回收(如果可用)
if (window.gc) {
window.gc();
}
}
// 清除所有定时器
private clearAllTimers(): void {
// 获取所有定时器ID
// 这里需要在创建定时器时记录
}
// 清除缓存
private clearCaches(): void {
// 清除内存缓存
if ('caches' in window) {
caches.keys().then(keys => {
keys.forEach(key => caches.delete(key));
});
}
}
// 监控内存使用
monitorMemoryUsage(interval: number = 5000): void {
setInterval(() => {
if (performance.memory) {
const memory = performance.memory as PerformanceMemory;
// 检查内存使用率
const usageRatio = memory.usedJSHeapSize / memory.jsHeapSizeLimit;
if (usageRatio > 0.8) {
// 内存使用率过高,执行清理
this.cleanup();
}
// 上报内存指标
console.log(`内存使用率: ${(usageRatio * 100).toFixed(2)}%`);
}
}, interval);
}
}
// 使用示例
const memoryOptimizer = MemoryOptimizer.getInstance();
// 在React组件中使用
export const withMemoryOptimization = <P extends object>(
WrappedComponent: React.ComponentType<P>
): React.FC<P> => {
return (props: P) => {
useEffect(() => {
// 组件挂载时注册监听器
return () => {
// 组件卸载时清理
memoryOptimizer.cleanup();
};
}, []);
return <WrappedComponent {...props} />;
};
};
5.2 长任务调度优化
kotlin
// performance/runtime/TaskScheduler.ts - 任务调度器
export class TaskScheduler {
private static instance: TaskScheduler;
private taskQueue: Array<ScheduledTask> = [];
private isProcessing = false;
private constructor() {}
static getInstance(): TaskScheduler {
if (!this.instance) {
this.instance = new TaskScheduler();
}
return this.instance;
}
// 调度任务
scheduleTask(task: ScheduledTask): void {
this.taskQueue.push(task);
this.processQueue();
}
// 处理任务队列
private async processQueue(): Promise<void> {
if (this.isProcessing) return;
this.isProcessing = true;
while (this.taskQueue.length > 0) {
// 获取下一个任务
const task = this.taskQueue.shift();
if (!task) break;
// 检查是否应该执行