第 6 章 性能优化:让页面快到飞起
Google 给了三个硬性指标叫 Core Web Vitals,直接影响 SEO 和转化率:LCP(最大内容绘制 < 2.5s)、INP(交互到下次绘制 < 200ms)、CLS(累积布局偏移 < 0.1)。1 秒延迟能少 7% 转化。
加载性能:让包变小、加载变快
- 代码分割:路由级懒加载
React.lazy+Suspense,首屏只下当前页面的 JS。 - Tree Shaking:Rollup/Vite 自动删掉没用到的代码,前提是用 ESM 别用 CommonJS。
- 压缩:服务器开 gzip / brotli 传输。
- 图片优化:用 WebP / AVIF 格式;
srcset响应式图片;loading="lazy"懒加载;写 width/height 防布局跳动。 - 字体优化:
font-display: swap先用系统字再换 Web 字体;preload预加载关键字体;子集化只留用到的字。 - 第三方脚本:广告、统计代码加
async/defer,或用 Partytown 放到 Web Worker。 - 缓存:HTTP 强缓存 + 协商缓存,CDN 分发。
路由懒加载
运行时性能:别让主线程堵死
- 避免无谓重渲染:
React.memo/useMemo/useCallback,但别过早优化,先看 DevTools Profiler。 - 长列表:几千条数据用虚拟列表(
react-window),只渲染可视区。 - 防抖 / 节流:搜索输入防抖(停 300ms 才发请求)、滚动事件节流。
- Web Worker:CPU 密集计算放后台线程,不阻塞 UI。
new Worker()+postMessage。 - 动画用 transform/opacity:不触发重排,只触发合成。
内存:别让浏览器越用越卡
① 定时器没清:setInterval 在 useEffect 里没返回 clear。
② 事件监听没移除:addEventListener 配对 removeEventListener。
③ 闭包持有大对象:不用的引用要置 null。
用 Chrome DevTools → Memory 面板拍堆快照,对比两次快照能看到谁没被回收。
分析工具:先测量再优化
Lighthouse 一键跑分给建议;Performance 面板 录一次操作看哪里卡;Performance Insights 自动找长任务。别靠感觉优化,靠数据。
Core Web Vitals 三指标深挖
| 指标 | 含义 | 合格线 | 怎么优化 |
|---|---|---|---|
| LCP | 最大内容绘制(首屏主元素出来) | < 2.5s | 优化字体/图片懒加载/预加载关键资源/CDN/减少阻塞的 JS CSS/SSR。 |
| INP | 交互到下一次绘制(替代 FID) | < 200ms | 减少主线程长任务/代码分割/防抖节流/避免布局抖动/虚拟列表/Worker。 |
| CLS | 累积布局偏移(页面乱跳) | < 0.1 | 图片 video 写 width/height/font-display:swap/广告位预留空间/用 transform 动画。 |
加载性能:关键路径与资源策略
- 代码分割:路由级 React.lazy+Suspense,Vite manualChunks 把 react/antd 等不变库单独分包(更新后不全量失效)。
- 图片:WebP/AVIF + srcset 响应式 + loading="lazy" 懒加载 + 写死宽高防 CLS。
- 字体:font-display:swap + 预加载 + 子集化(fonttools)。
- 传输:HTTP/2 多路复用、Gzip/Brotli 压缩、CDN 分发。
运行时:主线程别堵死
渲重排 vs 重绘 vs GPU 合成
重排(reflow):改 width/height/top/left/margin 等几何属性,浏览器要重新算布局,最贵。重绘(repaint):改 color/background/visibility,只画不改布局。GPU 合成:只改 transform/opacity,走合成线程,不碰主线程,最丝滑。动画永远用 transform/opacity,别用 left/width。
- 防抖/节流:搜索输入防抖(停 300ms 才请求)、滚动 resize 节流。
- 长列表:react-window / @tanstack/react-virtual 只渲染可视区。
- Web Worker:CPU 密集计算丢后台线程,postMessage 通信,不阻塞 UI。
- 内存泄漏三大件:定时器没 clear、事件监听没 remove、闭包持有大对象。用 DevTools Memory 拍堆快照对比。
性能监控
web-vitals 库把 LCP/INP/CLS 上报后端做真实用户监控(RUM);Lighthouse 实验室跑分给建议;Sentry 接性能监控 + 错误 + Source Map 还原堆栈。CI 里跑 Lighthouse CI 设性能预算(JS < 200KB、LCP < 2.5s),超了直接拦发布。
Q1. LCP/INP/CLS 各衡量什么?合格线?
看答案
LCP 首屏主内容绘制 <2.5s;INP 交互响应 <200ms;CLS 布局偏移 <0.1。
Q2. defer 和 async 的区别?
看答案
两者都不阻塞 HTML 解析;defer 下载完等 DOM 解析完按序执行;async 下完立刻执行,无序,互相无依赖的脚本用 async。
Q3. 为什么动画要用 transform 而不是 left?
看答案
改 left 触发重排重绘、走主线程;transform 走 GPU 合成,不触布局,帧率高不卡。
Q4. 虚拟列表解决什么问题?
看答案
长列表几千条 DOM 节点渲染/重排开销大,虚拟列表只渲染可视区的几十条,滚动时动态替换。