前端性能优化实战:从指标监控到极致体验

前端性能优化实战:从指标监控到极致体验

引言

性能优化不是简单的"加个缓存"或"压缩一下图片",而是一个系统性的工程。当你的应用面临百万级用户、复杂业务逻辑、多变网络环境时,每一个毫秒的优化都可能带来显著的业务提升。

本文将从五个维度展开:

  • 性能指标体系:从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;

      // 检查是否应该执行
相关推荐
deli0071 小时前
0.1 加 0.2 为什么不等于 0.3?IEEE 754 双精度逐位实测
前端
Momo__1 小时前
前端 Code Review 实战:识别 AI 写代码的 15 个常见坑
前端·vibecoding
拖孩1 小时前
全程 AI 写的小程序,刚接上会员虚拟支付,上线第二天就有人真金白银买单了|双签名、定价、iOS 的坑我全踩了一遍
前端·后端·微信小程序
小蚂蚁i1 小时前
React 基础
前端
ZoeLandia1 小时前
TypeScript 02:interface、type 与类型收窄
前端·typescript
Bode_20021 小时前
构建制造业知识库
前端·智能制造系统·业务流程再造
cindershade1 小时前
接口协作的交接面:让并行开发从约定走到发布
前端
JAVA老架构AI智能化1 小时前
多图叠加比对
前端·javascript·chrome
ShineWinsu1 小时前
对于HTML:HTML常见标签的解析—上
前端·面试·html·浏览器·标签·图片·超链接