前端性能优化

What — 是什么

前端性能优化是从网络、渲染、资源、代码四个维度提升页面加载速度和交互流畅度的系统性工程。

核心概念:

  • Core Web Vitals:LCP(最大内容绘制)、FID/INP(交互延迟)、CLS(布局偏移)
  • 加载优化:资源压缩、CDN、缓存、代码分割、懒加载
  • 渲染优化:减少重排重绘、虚拟列表、requestAnimationFrame
  • 网络优化:HTTP/2、预加载、预连接、减少请求数

关键特性:

  • LCP < 2.5s / INP < 200ms / CLS < 0.1 为良好
  • 优化应先度量再优化(DevTools / Lighthouse / Web Vitals)
  • 性能是持续关注,不是一次性的

Why — 为什么

适用场景:

  • 所有面向用户的 Web 应用
  • SEO 排名(Google 将 Core Web Vitals 纳入排名因素)
  • 用户留存(每慢 1s 转化率下降 7%)

优化维度:

维度目标关键手段
网络传输减少传输量/次数压缩、CDN、HTTP/2、缓存
资源加载减少关键路径资源代码分割、懒加载、预加载
渲染减少重排重绘transform、虚拟列表、will-change
运行时减少 JS 执行时间防抖节流、Web Worker、按需计算

How — 怎么用

快速上手

资源加载优化:

<head>
    <!-- DNS 预解析 -->
    <link rel="dns-prefetch" href="//api.example.com">
    <!-- 预连接 -->
    <link rel="preconnect" href="//cdn.example.com">
    <!-- 预加载关键资源 -->
    <link rel="preload" href="/fonts/main.woff2" as="font" crossorigin>
    <!-- 预获取下一页资源 -->
    <link rel="prefetch" href="/next-page.js">
</head>

代码示例

代码分割与懒加载:

// React 路由懒加载
const Dashboard = React.lazy(() => import('./pages/Dashboard'));
const Settings = React.lazy(() => import('./pages/Settings'));

<Suspense fallback={<Loading />}>
    <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
    </Routes>
</Suspense>

图片优化:

<!-- 懒加载 -->
<img src="photo.jpg" loading="lazy" alt="Photo">

<!-- 响应式图片 + 现代格式 -->
<picture>
    <source type="image/avif" srcset="photo.avif">
    <source type="image/webp" srcset="photo.webp">
    <img src="photo.jpg" alt="Photo" width="800" height="600"
         loading="lazy" decoding="async">
</picture>

<!-- Next.js Image 组件(自动优化) -->
<Image src="/hero.jpg" alt="Hero" width={1200} height={600} priority />

虚拟列表(大列表渲染):

import { useVirtualizer } from '@tanstack/react-virtual';

function BigList({ items }) {
    const parentRef = useRef();
    const virtualizer = useVirtualizer({
        count: items.length,
        getScrollElement: () => parentRef.current,
        estimateSize: () => 50,
    });

    return (
        <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
            <div style={{ height: virtualizer.getTotalSize() }}>
                {virtualizer.getVirtualItems().map(item => (
                    <div key={item.key}
                         style={{ position: 'absolute', top: item.start, height: item.size }}>
                        {items[item.index].name}
                    </div>
                ))}
            </div>
        </div>
    );
}

防抖与节流:

// 防抖:连续触发只执行最后一次
function debounce(fn, delay) {
    let timer;
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => fn(...args), delay);
    };
}

// 节流:固定间隔执行一次
function throttle(fn, interval) {
    let lastTime = 0;
    return (...args) => {
        const now = Date.now();
        if (now - lastTime >= interval) {
            lastTime = now;
            fn(...args);
        }
    };
}

// 使用
input.addEventListener('input', debounce(search, 300));
window.addEventListener('scroll', throttle(updatePosition, 100), { passive: true });

常见问题与踩坑

问题原因解决方案
LCP 过大关键资源阻塞渲染内联关键 CSS,预加载关键图片,减少阻塞 JS
CLS 偏移图片/广告/动态内容无预留空间设置 width/heightaspect-ratio
首屏 JS 过大未做代码分割路由懒加载 + 动态 import
滚动卡顿大列表 DOM 节点过多虚拟列表

最佳实践

  • 先用 Lighthouse 评分,针对性优化
  • 图片用 WebP/AVIF + 懒加载 + 尺寸预留
  • 路由级代码分割是标配
  • 防抖搜索、节流滚动、虚拟大列表

面试题

Q1: Core Web Vitals 包含哪些指标?各自衡量什么?

Core Web Vitals 包含三个指标:LCP(Largest Contentful Paint)衡量加载性能,目标 < 2.5s;INP(Interaction to Next Paint)衡量交互响应性,目标 < 200ms;CLS(Cumulative Layout Shift)衡量视觉稳定性,目标 < 0.1。其中 INP 已取代 FID 成为新标准。

Q2: 首屏优化有哪些常见方案?

核心思路是减少关键路径资源:路由级代码分割(动态 import)减少 JS 体积;关键 CSS 内联、非关键 CSS 异步加载;图片用 WebP/AVIF + 懒加载 + 预加载首屏图片;服务端渲染(SSR)或静态生成(SSG)加速首屏绘制;CDN + HTTP/2 减少网络延迟。

Q3: 虚拟列表的原理是什么?为什么能优化大列表渲染?

虚拟列表只渲染可视区域内的 DOM 节点,通过监听滚动事件计算当前视口应显示的列表项索引,动态替换渲染内容,并用绝对定位或 transform 偏移模拟完整滚动位置。万级数据列表仅渲染几十个 DOM 节点,显著减少重排重绘开销。

Q4: 代码分割有哪些策略?

路由级分割(React.lazy + 动态 import 按路由拆分);第三方库分割(splitChunksnode_modules 单独打包为 vendor chunk);组件级分割(条件渲染的弹窗/面板用动态 import);按功能模块分割(将不同业务模块拆为独立 chunk 按需加载)。


相关链接: