本文档使用AI工具将原始的手工笔记进行了整合, 并由AI进行评审后统一修正、重组与补全。
目录
1. 优化总览
前端性能优化围绕四条主线展开:
| 优化方向 | 目标 | 核心手段 |
|---|---|---|
| 减少网络交互次数 | 降低 RTT 开销 | 资源合并、缓存、协议升级 |
| 减少传输内容大小 | 降低带宽消耗 | 压缩、精简、按需加载 |
| 优化渲染路径 | 加速首屏呈现 | 关键渲染路径优化、异步加载 |
| 架构与基础设施 | 全局提效 | CDN、HTTP/2、边缘计算 |
2. 性能指标体系
架构师点评: 原文档使用自造术语,此处统一映射到 Web Vitals 标准指标。
2.1 核心 Web Vitals(Google 标准)
| 指标 | 原文映射 | 含义 | 目标阈值 |
|---|---|---|---|
| FCP (First Contentful Paint) | 首次渲染 | 浏览器从白屏到首次绘制任何内容 | ≤ 1.8s |
| LCP (Largest Contentful Paint) | 首次有意义渲染 [1] | 视口内最大内容元素渲染完成 | ≤ 2.5s |
| TTI (Time to Interactive) | 可交互时间 | 页面完全可交互(主线程空闲 ≥5s 窗口) | ≤ 3.8s |
| TBT (Total Blocking Time) | — | FCP 到 TTI 之间主线程被长任务阻塞的总时长 | ≤ 200ms |
| CLS (Cumulative Layout Shift) | — | 页面生命周期内的累计布局偏移 | ≤ 0.1 |
| INP (Interaction to Next Paint) [2] | — | 用户交互到下一帧绘制的延迟 | ≤ 200ms |
[1] FMP(First Meaningful Paint)已被 Lighthouse 弃用,改用 LCP。
[2] INP 于 2024 年 3 月取代 FID 成为 Core Web Vitals 指标。
2.2 其他参考指标
| 指标 | 含义 |
|---|---|
| Visually Complete | 视口内所有内容可见(原始文档中的"视觉完整") |
| Speed Index | 页面内容可见填充速度的综合得分 |
| TTFB (Time to First Byte) | 首字节时间,反映服务端响应速度 |
3. 网络传输优化
3.1 减少网络交互次数
(a)请求合并
| 手段 | 说明 |
|---|---|
| 公共资源提取 | 将共用 JS/CSS 抽取为公共 chunk,多页面共享,避免重复加载 |
| 防止重复加载 | 确保同一页面内不会多次加载相同脚本(Webpack SplitChunksPlugin / Vite 天然支持) |
| CSS Sprites | 将多张小图合并为一张雪碧图,通过 background-position 定位 |
| 小图内联 | 图片经 Base64 编码后体积 < 5KB 的,可直接内联到 HTML/CSS 中,省去一次请求 |
| 按需打包 | 使用现代构建工具(Webpack / Vite / esbuild)将多个模块打包为有限 bundle,配合 Code Splitting 按路由拆分 |
(b)协议升级
- HTTP/2 多路复用:单 TCP 连接上并行传输多个请求/响应,消除 HTTP/1.1 的队头阻塞。
- HTTP/3 (QUIC):基于 UDP,进一步解决 TCP 层的队头阻塞,连接迁移支持更好。
- WebSocket: 服务端推送和双向实时通信场景
- SSE(Server-Sent Events): 服务端主动推送的场景应优先考虑
(c)减少重定向
每次 301/302 重定向消耗一次 RTT,首屏关键路径上应杜绝重定向。
(d)DNS 预解析与预连接
|
|
3.2 减少传输内容大小
| 手段 | 说明 |
|---|---|
| Gzip / Brotli 压缩 | 文本资源(HTML、CSS、JS、JSON、SVG)开启 Brotli(压缩率优于 Gzip ~20%) |
| 代码精简 | 去除注释、空白符、死代码(Tree Shaking);生产构建开启 minify |
| 图片格式升级 | 照片/复杂图 → WebP 或 AVIF(压缩率更高);矢量/图标 → SVG;动图 → APNG 或 Lottie,避免 GIF |
| 视频格式 | 优先 WebM(VP9 编码)或 AV1,H.264 为兼容兜底 |
| 响应式图片 | 根据设备分辨率/屏幕尺寸返回合适尺寸的图片(srcset + sizes) |
| 缩略图 + 渐进加载 | 先加载低质量缩略图(LQIP),用户交互时再异步加载原图 |
| Cookie 瘦身 | 不在根域存放过多 Cookie,通过子域隔离;静态资源使用无 Cookie 域名 |
| 条件请求 | 利用 Cache-Control / ETag / Last-Modified 使浏览器在资源未变化时使用 304 响应,避免重复传输 |
4. 渲染层优化
4.1 关键渲染路径优化
- CSS 置于
<head>中:让浏览器尽早构建 CSSOM,避免样式表阻塞渲染。 - JavaScript 异步加载:
<script defer>:后台下载,HTML 解析完成后、DOMContentLoaded前按序执行(推荐)。<script async>:后台下载,下载完立即执行,不保证顺序(适合独立脚本,如统计 SDK)。- 传统做法(
<script>置于</body>前)可接受,但defer更优。
- 避免
@import加载 CSS:@import会使 CSS 文件串行加载(需等父 CSS 解析到该语句时才发起请求),增加关键路径长度。- 推荐使用
<link rel="stylesheet">并行加载。
4.2 避免渲染阻塞
| 问题 | 解决方案 |
|---|---|
空的 href / src |
<link href=""> 或 <img src=""> 等空属性标签会使浏览器尝试加载当前页面 URL,浪费带宽并阻塞其他资源——发现即删除或填补 |
| 首屏无关逻辑 | 延迟加载(requestIdleCallback / 路由级 Code Splitting) |
| JS 长任务阻塞渲染 | 将大计算拆分为多个小任务(Time Slicing),或放入 Web Worker |
4.3 减少重排(Reflow)与重绘(Repaint)
- 批量 DOM 操作:使用
DocumentFragment或display: none包裹 → 修改 → 显示,避免多次触发重排。 - 避免逐项修改样式:使用
classList一次性切换类名,而非逐个设置style属性。 - 用 CSS 动画替代 JS 动画:CSS 动画运行在合成器线程(Compositor Thread),不占用主线程;JS 动画频繁操作 DOM 极易引发重排。
- 使用
transform和opacity:这两个属性只触发合成(Composite),不触发重排/重绘,是高性能动画的首选。 - 不在 HTML 中直接缩放图片:
<img width="200">让浏览器下载原图后缩放,同时可能引发重排;应使用实际尺寸图片或srcset。 - 读写分离:避免在同一个帧内穿插读取布局属性(如
offsetHeight)和修改样式,这会强制同步布局(Forced Synchronous Layout)。
4.4 DOM 结构优化
- 减少 DOM 深度与数量:过深的 DOM 树增加解析和样式计算成本。单页 DOM 节点数建议 < 1500。
- CSS 选择器优化:
- 浏览器从右向左匹配选择器,末尾通配符(
*)会导致匹配所有元素后再回溯,应避免:.list * {}❌ → 使用具体类名 ✅。 - 减少嵌套选择器层级:
.a .b .c .d {}❌ →.d--specific {}✅(BEM 等命名约定)。
- 浏览器从右向左匹配选择器,末尾通配符(
- 关于
<table>的正确认知:架构师点评(原文档错误修正): 原始文档建议"尽量避免使用
<table>"。在现代浏览器中,<table>的渲染性能已大幅改善。表格应用于展示表格数据是完全合理的,不应禁止。真正应禁止的是用<table>做页面布局(这是 2000 年代的过时做法)。长表格的分页/虚拟滚动是更值得关注的优化点。 - 关于
<iframe>:<iframe>会阻塞父页面onload事件;- 与父页面共享同域连接池,影响并行加载;
- SEO 不友好(搜索引擎不易抓取 iframe 内内容);
- 如需使用,通过 JS 动态设置
src属性可规避部分问题; - 现代场景中,微前端方案(如 Module Federation、qiankun)是对 iframe 的更优替代。
4.5 图片懒加载
推荐使用原生懒加载(Chrome 77+):
|
|
传统 JS 方案(Intersection Observer)仅在需要兼容旧浏览器或需要更复杂行为(如占位图过渡动画)时使用。
4.6 脚本优化
-
DOM 查询优化:
- 优先使用
getElementById和querySelector(由快到慢:ID > Class > Tag > 复杂选择器)。 - 缓存查询结果,避免重复遍历 DOM 树。
1 2 3 4 5 6 7 8// ❌ 不推荐:重复查询 document.querySelector('#mod .active').classList.remove('active'); document.querySelector('#mod .inactive').classList.add('active'); // ✅ 推荐:缓存容器引用 const mod = document.querySelector('#mod'); mod.querySelector('.active').classList.remove('active'); mod.querySelector('.inactive').classList.add('active'); - 优先使用
-
静态资源分域存放:
- 浏览器对同域名的并发连接数有限制(HTTP/1.1 下通常 6 个);
- 将资源分布到不同子域名(如
static1.example.com、static2.example.com)或 CDN 域名可增加并行下载数; - 但 HTTP/2 下此策略反效果——多路复用使单连接即可并行,多域名反而增加 DNS 查询和 TCP 握手开销。仅 HTTP/1.1 用户占比较高时考虑保留。
5. 缓存策略
5.1 页面分类与缓存方案
| 页面类型 | 特征 | 推荐策略 |
|---|---|---|
| 纯静态页面 | 几乎不更新(如"关于我们") | CDN 长缓存 + Cache-Control: max-age=31536000, immutable |
| 纯动态页面 | 每次/每人看到不同内容 | 不做页面级缓存;做数据层缓存(Redis / 本地存储)以加速 API 响应 |
| 短时静态页面 | 一段时间内不变,可容忍短期不一致 | 服务端渲染后缓存为静态文件或写入公共缓存层;更新时主动清除(Cache-Aside 模式) |
| 动静结合页面 | 同一页面混合静态区域与动态区域 | 静态部分走 CDN/服务端缓存;动态部分异步请求 + 客户端渲染(SSI / ESI / CSR 组合) |
5.2 数据缓存
- 缓存粒度:JSON 数据、局部 DOM 片段、或 API 响应体。
- 客户端缓存层:
localStorage/sessionStorage/ IndexedDB / Service Worker Cache API。 - 服务端缓存:Redis / Memcached 缓存 API 响应或 DB 查询结果。
5.3 缓存注意事项
- 设置合理的过期时间:根据数据新鲜度需求权衡(静态资源用强缓存 + 文件名 Hash 实现永久缓存)。
- 主动失效机制:CDN 厂商通常提供 Purge API,内容更新时应主动触发刷新。
- 防止缓存击穿 / 雪崩:
- 缓存击穿:热点 key 过期瞬间大量请求直达 DB → 互斥锁 / 永不过期 + 异步刷新。
- 缓存穿透:请求不存在的数据 → 布隆过滤器 / 空值缓存。
- 缓存雪崩:大量 key 同时过期 → 过期时间加随机偏移(TTL Jitter)。
- 缓存踩踏(原文档描述的"第一个无缓存请求时其他阻塞"):又称 Cache Stampede,首请求构建缓存期间其他并发请求应等待而非各自回源 → 使用 Promise 共享或锁机制。
- 多端缓存隔离:根据
User-Agent区分 PC / Mobile / App 内嵌 WebView,分别缓存;或通过不同子域名独立服务。
6. 架构与基础设施
6.1 CDN 部署
- 静态资源(JS、CSS、图片、字体)部署到 CDN,利用边缘节点就近服务。
- 选择支持 HTTP/2 和 Brotli 的 CDN 厂商。
6.2 HTTP 协议演进
| 协议 | 关键特性 | 推荐场景 |
|---|---|---|
| HTTP/1.1 | Keep-Alive、管道化(实践中不可靠) | 仅兼容性要求时保留 |
| HTTP/2 | 多路复用、Server Push(已逐步弃用)、Header 压缩 | 当前主流,推荐默认开启 |
| HTTP/3 (QUIC) | UDP 传输、0-RTT 建连、连接迁移 | 移动端及弱网环境下收益最大 |
6.3 构建与工程化
- Code Splitting:按路由/组件拆分代码,首屏只加载必要 bundle。
- Tree Shaking:消除未使用的模块导出。
- 资源 Hash:文件名带内容 Hash(如
app.a3f8c.js),配合max-age=31536000实现永久缓存,更新时自然失效。 - 预加载关键资源:
<link rel="preload">提前声明首屏关键 CSS/字体/图片。
7. 移动端专项优化
移动端核心矛盾:网络波动 + 设备性能差异 + 首屏速度敏感。
7.1 首屏优化
- 控制首屏资源总量:首屏 HTML + 关键 CSS + 首屏 JS 总体积建议 < 200KB(压缩后)。
- 首屏静态化 / 服务端渲染(SSR):将首屏 HTML 在服务端生成,避免"JS 加载 → 请求数据 → 渲染"的串行等待链。
- 首屏关键资源内联:关键 CSS 内联到
<head>的<style>标签中;少量关键 JS 也可内联,避免额外 RTT。 - 非首屏内容延迟加载:图片懒加载、路由懒加载、组件按需加载。
- 提前推送资源: 在活动开始之前就推送相关静态资源到客户端;
7.2 移动端适配
- 使用
srcset+sizes属性根据屏幕宽度返回适当尺寸图片。 - 触摸交互优化:消除 300ms 点击延迟(
<meta name="viewport" content="width=device-width">),使用touch-action控制手势行为。
8. 附录:Web 字体
8.1 字体格式选择
| 格式 | 压缩率 | 浏览器支持 | 推荐 |
|---|---|---|---|
| WOFF2 | 最优(Brotli 压缩) | 现代浏览器全覆盖 | ✅ 首选 |
| WOFF | 较好 | 几乎全部浏览器 | ✅ 兜底 |
| TTF/OTF | 无压缩 | 最广 | ❌ Web 不推荐(体积大) |
| EOT | — | 仅旧版 IE | ❌ 已淘汰 |
8.2 最佳实践
|
|
关键要点:
font-display: swap:立即使用回退字体渲染文本,字体加载完成后切换。避免不可见文本闪烁(FOIT)。- 子集化(Subsetting):如果只使用部分字符(如中文网站仅常用 3500 字),从字体文件中裁剪出子集,体积可减少 70-90%。
- 预加载:
<link rel="preload" as="font" crossorigin>提前声明字体下载。 - 使用
unicode-range:对不同字重/样式指定不同字体文件,浏览器仅下载实际使用的字符集的字体。 - 仅引入所需字重:一个常规(400)+ 一个粗体(700)通常已满足大部分设计需求。
本文档由原始目录 架构设计/6-软件-前端/ 下的五个文件合并、评审与重写而来。原文件中标记为错误的观点已修正,结构已按专业架构文档标准重新编排。