Develop

前端性能优化全链路:从 Lighthouse 分析到代码落地的完整指南

✎ -- 字 🕐 -- 分钟
字号
前端性能优化全链路构建阶段Tree ShakingCode Splitting网络阶段HTTP/2CDN & 缓存渲染阶段懒加载虚拟列表交互阶段Web Worker防抖节流

前言

性能优化不是"感觉很快",而是可度量的。本文以 Lighthouse 报告为起点,拆解从构建到运行时的完整优化链路,每一步都配有可落地的代码和配置。

一、读懂 Lighthouse 报告

Lighthouse 六大核心指标:

指标含义优秀阈值
FCP (First Contentful Paint)首次内容绘制< 1.8s
LCP (Largest Contentful Paint)最大内容绘制< 2.5s
TBT (Total Blocking Time)总阻塞时间< 200ms
CLS (Cumulative Layout Shift)累计布局偏移< 0.1
Speed Index速度指数< 3.4s
TTI (Time to Interactive)可交互时间< 3.8s

二、构建阶段优化

2.1 Tree Shaking

// 确保 package.json 中声明 sideEffects
{
  "name": "my-lib",
  "sideEffects": false  // 或 ["*.css"] 如果有副作用文件
}

// Vite 默认开启,Webpack 需配置 mode: 'production'

2.2 Code Splitting(代码分割)

// React 路由级别懒加载
import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}

// Vite 手动分割配置
// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'react-vendor': ['react', 'react-dom', 'react-router-dom'],
          'ui-vendor': ['antd', '@ant-design/icons'],
        }
      }
    }
  }
});

2.3 图片优化

// Vite 配置自动压缩
import viteImagemin from 'vite-plugin-imagemin';

export default defineConfig({
  plugins: [
    viteImagemin({
      gifsicle: { optimizationLevel: 3 },
      mozjpeg: { quality: 80 },
      pngquant: { quality: [0.7, 0.8] },
      svgo: { plugins: [{ removeViewBox: false }] }
    })
  ]
});

// 使用现代图片格式
<picture>
  <source srcSet="hero.avif" type="image/avif" />
  <source srcSet="hero.webp" type="image/webp" />
  <img src="hero.jpg" alt="hero" loading="lazy" />
</picture>

三、网络阶段优化

3.1 资源预加载策略

<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="https://api.example.com" />

<!-- 预连接(DNS + TCP + TLS)-->
<link rel="preconnect" href="https://fonts.googleapis.com" />

<!-- 关键资源预加载 -->
<link rel="preload" href="/fonts/main.woff2" as="font" crossorigin />

<!-- 预获取下一页资源 -->
<link rel="prefetch" href="/page-2.js" as="script" />

3.2 缓存策略

# Nginx 静态资源缓存配置
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# HTML 不缓存
location / {
    add_header Cache-Control "no-cache";
}

四、渲染阶段优化

4.1 虚拟列表

// 万级列表渲染方案
function VirtualList({ items, itemHeight, containerHeight }) {
  const [scrollTop, setScrollTop] = useState(0);
  
  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(
    startIndex + Math.ceil(containerHeight / itemHeight) + 1,
    items.length
  );
  
  const visibleItems = items.slice(startIndex, endIndex);
  
  return (
    <div style={{ height: containerHeight, overflow: 'auto' }}
         onScroll={e => setScrollTop(e.target.scrollTop)}>
      <div style={{ height: items.length * itemHeight, position: 'relative' }}>
        {visibleItems.map((item, i) => (
          <div key={startIndex + i}
               style={{ position: 'absolute', top: (startIndex + i) * itemHeight, height: itemHeight }}>
            {item}
          </div>
        ))}
      </div>
    </div>
  );
}

4.2 图片懒加载

<!-- 原生懒加载,兼容率 93%+ -->
<img src="placeholder.jpg" data-src="real.jpg" loading="lazy" />

<!-- Intersection Observer 精确控制 -->
<script>
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
});

document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));
</script>

五、运行时性能优化

5.1 React 渲染优化

// useMemo — 缓存计算结果
const sortedList = useMemo(
  () => items.sort((a, b) => a.name.localeCompare(b.name)),
  [items] // 仅 items 变化时重新计算
);

// useCallback — 稳定函数引用
const handleClick = useCallback((id: number) => {
  setSelectedId(id);
}, []); // 不依赖外部变量,引用永远不变

// React.memo — 避免无关渲染
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
  return <div>{/* 复杂渲染 */}</div>;
});

// 使用 useDeferredValue 降低更新优先级
import { useDeferredValue } from 'react';
const deferredQuery = useDeferredValue(searchQuery);
// 用 deferredQuery 渲染列表,searchQuery 更新输入框

5.2 Web Worker 释放主线程

// worker.ts
self.onmessage = (e: MessageEvent<number[]>) => {
  const result = e.data
    .filter(n => n > 1000)
    .map(n => n * 2)
    .reduce((a, b) => a + b, 0);
  self.postMessage(result);
};

// main.ts
const worker = new Worker(new URL('./worker.ts', import.meta.url));
worker.postMessage(largeArray);
worker.onmessage = (e) => console.log('结果:', e.data);

优化效果检查清单

优化项方法预期收益
Bundle 体积Tree Shaking + Code Split首屏 JS -40%~60%
图片加载WebP/AVIF + 懒加载 + CDNLCP -30%~50%
字体加载font-display: swap + preload消除 FOIT/FOUT
长列表虚拟列表内存 -90%,FPS 稳定 60
重复渲染React.memo + useMemoTBT -50%~70%

性能优化的黄金法则:先测量,再优化,再测量。不要凭感觉优化,Chrome DevTools Performance 面板是你最好的朋友。