页面性能优化不是简单的“压缩一下资源”,而是一套从度量→定位→治理→验收的体系化问题。本文结合实际落地经验,从度量指标、资源体积、网络请求、渲染路径、用户感知与工程化保障几个维度,梳理一条可执行的优化路径。
一、先谈“怎么量”——关键指标
- Core Web Vitals:
- LCP(Largest Contentful Paint):最大内容绘制时间,衡量首屏主要内容何时可见。
- INP(Interaction to Next Paint):交互到下一帧的时延,替代 FID 评估交互响应性。
- CLS(Cumulative Layout Shift):累积布局偏移,衡量页面稳定性。
- 其他常用指标:TTFB、FCP、TTI、TBT、FP/FMP、JS long task 数量等。
建议:本地用 Lighthouse 建基线,线上用 RUM(真实用户监控)采样埋点,按设备/网络分层观察。
二、资源体积优化(越少越好)
1. JavaScript:减少、拆分、可缓存
- Tree Shaking / Dead Code Elimination:确保使用 ESM、移除无用导出。
- Code Splitting:按路由/功能动态加载,降低首屏负担。
1 2
| import('~/pages/settings').then(({ render }) => render());
|
- 第三方依赖瘦身:
- 替换大库(如 moment → dayjs/luxon)。
- 只引入子模块(lodash-es 按需)。
- 开启 bundler 的现代产物(module/exports 字段、esm 优先)。
2. 压缩与传输编码
- JS/CSS 开启代码压缩(Terser/CSSNano)。
- 服务端(或 CDN)开启 Brotli 优先,Gzip 兜底。
1 2 3 4 5 6
| gzip on; gzip_types application/javascript text/css application/json image/svg+xml; gzip_min_length 1024;
|
3. 图片与媒体
- 格式选型:优先 WebP/AVIF,按兼容性回退。
- 响应式与懒加载:
srcset/sizes
+ loading="lazy"
+ 显式 width/height
防 CLS。
1 2 3 4 5 6 7 8 9
| <img src="/img/hero.avif" srcset="/img/hero-640.avif 640w, /img/hero-1280.avif 1280w" sizes="(max-width: 768px) 100vw, 768px" loading="lazy" fetchpriority="high" width="1280" height="720" alt="hero" />
|
- 占位/渐进:LQIP/BlurHash;视频首帧海报 poster。
4. CSS 与字体
- Critical CSS:首屏关键样式内联/预加载;非关键样式延后应用。
1 2
| <link rel="preload" as="style" href="/css/critical.css" /> <link rel="stylesheet" href="/css/index.css" media="print" onload="this.media='all'" />
|
- Purge/Unused CSS:PurgeCSS/Tailwind JIT 等移除未使用选择器。
- 字体:子集化 +
font-display: swap
+ 预加载。
1 2 3 4 5
| @font-face { font-family: 'InterSubset'; src: url('/fonts/inter-subset.woff2') format('woff2'); font-display: swap; }
|
1
| <link rel="preload" as="font" href="/fonts/inter-subset.woff2" type="font/woff2" crossorigin>
|
三、网络请求优化(更快拿到、更少往返)
1. 协议与拓扑
- HTTP/2/3 多路复用、头部压缩;尽量走 CDN(就近、缓存、压缩、TLS 优化)。
- 合理的域名拆分,控制并发与连接复用。
2. 缓存策略
- 静态资源指纹化 +
Cache-Control: public, max-age=31536000, immutable
。
- 非指纹接口配合 ETag/Last-Modified,短缓存 + 协商。
1 2
| Cache-Control: public, max-age=31536000, immutable ETag: "68f4-5f1a..."
|
3. 预连接与预加载
1 2 3 4
| <link rel="dns-prefetch" href="//cdn.example.com"> <link rel="preconnect" href="https://cdn.example.com" crossorigin> <link rel="preload" as="script" href="/js/main.js"> <link rel="prefetch" href="/js/route-settings.chunk.js">
|
4. 阻塞外链与脚本
defer
优先、慎用 async
(有依赖顺序时)。
1
| <script src="/js/main.js" defer></script>
|
- 延迟/条件加载三方脚本(埋点、广告、A/B 等),必要时沙箱隔离。
- Service Worker:离线缓存、预缓存关键路径,回源兜底。
四、渲染与主线程优化(更快可见、更快可交互)
1. 关键渲染路径(CRP)
- 减少首屏 JS 执行量与 CSS 阻塞;SSR/SSG 提升首屏可见性;Hydration 分片或延后。
- 精简首屏 DOM 复杂度,避免大列表一次性渲染(使用虚拟列表/分页)。
2. 主线程“长任务”治理
- 将 >50ms 的长任务拆分到多个微任务/宏任务,或丢给 Web Worker。
1 2 3
| const worker = new Worker('/worker.js'); worker.postMessage({ type: 'calc', payload: bigData });
|
- 动画使用
transform/opactiy
,避免触发布局;使用 will-change
谨慎。
- 事件节流/防抖,减少同步布局抖动(Layout Thrashing)。
3. 优先级与调度
- 使用
requestIdleCallback
在空闲时做非关键工作;采用现代框架的并发特性/调度器。
- 网络/渲染优先级:
fetchpriority="high"
用于首图,priority hints
配合资源管理。
五、用户感知优化(看起来更快)
- Skeleton/占位:首屏骨架、图片模糊占位,避免“白屏恐惧”。
- 渐进展示:先上关键信息,再补充增强;列表先展示可见区域。
- 交互回馈:按钮点击即刻反馈(禁用态/Loading),避免用户重复操作。
- 错误/重试策略:弱网下的容错与降级,提供离线提示。
1 2
| <img src="/img/hero.jpg" fetchpriority="high" alt="hero" />
|
六、工程化与持续保障
- 性能预算(Performance Budget):为 JS/CSS/图片设定体积与指标预算,超标即失败。
- 分析工具:webpack-bundle-analyzer、Source Map 体积审计;依赖许可证与重复库检测。
- CI 守门:Lighthouse CI/Calibre/SpeedCurve 接入流水线;PR 侧边栏展示变化。
- RUM 监控:上报 LCP/CLS/INP、资源耗时、长任务、错误栈;按端/网络维度聚合。
- 实验治理:灰度/AB 测试,评估改动对指标的真实影响。
七、常见坑与对策
- 过多三方脚本 → 延迟/条件加载,或使用代理与沙箱;定期体检下线。
- 大而全 polyfill → 基于 UA/feature 的差异化注入(polyfill.io/self-host)。
- 单包过大 → 路由/组件分包、公共依赖拆分、动态导入。
- Reflow 频繁 → 批量读写 DOM(FastDom)、动画走合成层。
- 图片失衡 → 统一中台(裁剪/压缩/格式自动),规范宽高与密度。
八、落地清单(Checklist)
- 已建立 CWV 与性能预算基线,并接入线上 RUM。
- 首屏 JS < 170KB(gz/br 后)、首屏 CSS < 30KB;路由级分包。
- 图片 WebP/AVIF + 懒加载 + 宽高/占位;字体子集化 + preload。
- 静态资源带指纹且长缓存;接口协商缓存;预连接/预加载关键资源。
- 关键渲染路径可控:Critical CSS、defer 脚本、SSR/SSG 可选。
- 长任务监控 < 50ms;Worker/调度器落地;虚拟列表替换大渲染。
结语
性能优化是长期工程,遵循“度量驱动”的闭环——先测量、再优化、再验证,并通过工程化手段持续守住基线。把资源做小、请求提效、渲染加速、感知做足,最终会体现在稳定优秀的用户体验与业务转化上。
Happy Coding!