从性能优化到 HTTP 变化及缓存
笔试题(6 题)
1. HTTP/1.1 vs HTTP/2 vs HTTP/3
对比 HTTP/1.1、HTTP/2、HTTP/3 的核心差异,以及它们对前端性能优化的影响。
【作答】:
HTTP/1.1:
核心特性:
- 持久连接(Connection: keep-alive),减少连接建立开销
- 管道化(pipelining),但存在队头阻塞问题
- 支持分块传输编码(chunked transfer encoding)
- 支持缓存控制(Cache-Control、ETag等)
性能瓶颈:
- 队头阻塞(Head-of-Line Blocking):同一连接上请求必须按顺序响应
- 每个域名最多6个并发连接限制
- 请求头冗余,每次请求都携带完整头部
- 不支持服务器推送
HTTP/2:
核心特性:
- 多路复用(Multiplexing):单连接上并行处理多个请求/响应
- 二进制分帧:将数据分割为更小的帧,提高传输效率
- 头部压缩(HPACK):减少头部大小
- 服务器推送(Server Push):主动推送资源给客户端
- 流优先级:支持请求优先级设置
性能提升:
- 消除队头阻塞(应用层)
- 减少连接数,降低延迟
- 头部压缩可减少30-50%的头部大小
- 多路复用提高带宽利用率
HTTP/3:
核心特性:
- 基于UDP的QUIC协议,而非TCP
- 内置加密(TLS 1.3)
- 连接迁移:网络切换时保持连接
- 0-RTT连接建立(已连接过的服务器)
- 解决传输层队头阻塞问题
性能提升:
- 消除TCP层队头阻塞
- 更快的连接建立(0-RTT或1-RTT)
- 更好的移动网络体验(连接迁移)
- 减少延迟和丢包影响
对前端优化策略的影响:
HTTP/1.1时代:
- 需要合并文件、使用雪碧图、域名分片
- 内联关键CSS/JS
- 减少HTTP请求数
HTTP/2时代:
- 可以拆分文件,利用多路复用
- 不再需要雪碧图,可以单独请求图片
- 域名分片可能反而降低性能
- 服务器推送可以主动推送关键资源
HTTP/3时代:
- 进一步优化移动端体验
- 更快的连接建立,减少首次加载时间
- 更好的网络切换体验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2. HTTP 缓存机制
HTTP 缓存机制详解:强缓存(Expires、Cache-Control)vs 协商缓存(ETag、Last-Modified)。
【作答】:
强缓存:
Expires:
- HTTP/1.0的缓存控制,指定资源的过期时间(绝对时间)
- 格式:Expires: Wed, 21 Oct 2025 07:28:00 GMT
- 问题:依赖客户端时间,时间不准确会导致缓存失效
- 优先级低于Cache-Control
Cache-Control:
- HTTP/1.1的缓存控制,更灵活强大
- 常用指令:max-age、no-cache、no-store、public、private
- 示例:Cache-Control: max-age=3600, public
- 优先级高于Expires
协商缓存:
ETag:
- 实体标签,资源的唯一标识符(通常是内容的hash值)
- 格式:ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
- 客户端请求时携带If-None-Match头
- 服务器比较ETag,相同返回304,不同返回200和新ETag
- 优点:精确,能检测到内容变化
- 缺点:需要计算hash,消耗服务器资源
Last-Modified:
- 资源最后修改时间
- 格式:Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT
- 客户端请求时携带If-Modified-Since头
- 服务器比较时间,未修改返回304,已修改返回200
- 优点:实现简单
- 缺点:精度到秒,1秒内多次修改无法检测;文件内容未变但时间变了会失效
缓存流程:
1. 浏览器发起请求
2. 检查强缓存(Cache-Control/Expires)
- 未过期:直接使用缓存,状态码200(from cache)
- 已过期:进入下一步
3. 检查协商缓存(ETag/Last-Modified)
- 发送If-None-Match(ETag)或If-Modified-Since(Last-Modified)
- 服务器验证资源是否变化
- 未变化:返回304 Not Modified,使用缓存
- 已变化:返回200和新资源
优先级:
1. Cache-Control > Expires(强缓存)
2. ETag > Last-Modified(协商缓存)
3. 强缓存 > 协商缓存
4. 如果同时设置,浏览器优先使用Cache-Control和ETag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
3. Cache-Control 指令
Cache-Control 常用指令有哪些?max-age、no-cache、no-store、public、private 的区别?
【作答】:
max-age:
- 指定资源可以被缓存的最大时间(秒)
- 示例:Cache-Control: max-age=3600(缓存1小时)
- 相对时间,不依赖客户端时钟
- 从响应时间开始计算
no-cache:
- 不是"不缓存",而是"使用前必须验证"
- 每次使用缓存前都要向服务器验证(发送协商缓存请求)
- 适用于需要实时性但变化不频繁的资源
- 示例:Cache-Control: no-cache
no-store:
- 真正的不缓存,禁止任何缓存
- 不存储到浏览器缓存、CDN、代理服务器等任何地方
- 适用于敏感数据(密码、支付信息等)
- 示例:Cache-Control: no-store
public:
- 允许任何缓存(浏览器、CDN、代理服务器)缓存资源
- 适用于可以被公开缓存的资源(图片、字体、静态文件等)
- 示例:Cache-Control: public, max-age=31536000
private:
- 只允许浏览器缓存,不允许CDN、代理服务器缓存
- 适用于用户个性化内容(用户信息、购物车等)
- 示例:Cache-Control: private, max-age=3600
组合使用场景:
1. 静态资源(JS/CSS/图片):
Cache-Control: public, max-age=31536000, immutable
- 长期缓存,使用文件hash确保更新
2. HTML文件:
Cache-Control: no-cache
- 每次验证,确保获取最新版本
3. API响应(用户数据):
Cache-Control: private, max-age=300
- 短期缓存,仅浏览器缓存
4. 敏感数据:
Cache-Control: no-store, no-cache, must-revalidate
- 完全不缓存
5. 可缓存但需验证:
Cache-Control: public, max-age=3600, must-revalidate
- 缓存1小时,过期后必须验证
6. 其他指令:
- must-revalidate:过期后必须验证,不能使用过期缓存
- immutable:资源永不过期,适用于带hash的文件
- s-maxage:CDN/代理服务器的缓存时间
- stale-while-revalidate:允许使用过期缓存,同时后台更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
4. 缓存策略设计
如何为不同资源设计缓存策略(HTML、JS/CSS、图片、API)?文件指纹(hash)的作用?
【作答】:
HTML:
策略:no-cache 或 max-age=0, must-revalidate
原因:
- HTML是入口文件,必须保证获取最新版本
- HTML中引用的资源URL会变化(带hash),需要及时更新
- 使用no-cache确保每次验证,但304响应仍可使用缓存
实践:
- 设置较短的max-age(如300秒),过期后验证
- 或使用no-cache,配合ETag进行协商缓存
JS/CSS:
策略:public, max-age=31536000, immutable(配合文件hash)
原因:
- 静态资源变化频率低,适合长期缓存
- 使用文件hash(contenthash)确保内容变化时URL变化
- immutable告诉浏览器资源永不过期,避免不必要的验证请求
实践:
- 构建时生成hash:app.abc123.js
- 设置长期缓存(1年)
- HTML中引用带hash的URL,HTML更新时自动获取新资源
图片/字体:
策略:public, max-age=2592000(30天)
原因:
- 图片和字体文件较大,适合缓存
- 变化频率较低
- 可以设置较长的缓存时间
实践:
- 使用CDN加速
- 设置合理的max-age(1-6个月)
- 对于可能更新的图片,使用版本号或hash
API:
策略:根据数据类型选择
- 用户数据:private, max-age=0, must-revalidate
- 公共数据:public, max-age=300(5分钟)
- 实时数据:no-store 或 no-cache
原因:
- API数据变化频繁,需要根据业务场景设置
- 用户个性化数据不能公开缓存
- 需要平衡实时性和性能
实践:
- 使用ETag进行协商缓存
- 设置合理的缓存时间
- 考虑使用SWR(stale-while-revalidate)策略
文件指纹 (hash):
作用:
1. 内容变化时URL变化,强制浏览器获取新资源
2. 实现长期缓存:URL不变时使用缓存,URL变化时获取新资源
3. 解决缓存更新问题:无需手动清理缓存
4. 支持并行部署:新旧版本可以同时存在
实现方式:
- Webpack: [name].[contenthash:8].js
- Vite: 自动生成hash文件名
- 手动:version?v=1.2.3(版本号,不如hash精确)
最佳实践:
- JS/CSS使用contenthash(基于文件内容)
- 图片可以使用hash或版本号
- HTML中引用带hash的资源URL
- 配合immutable指令,避免不必要的验证请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
5. Service Worker 缓存
Service Worker 缓存与 HTTP 缓存的区别?SW 缓存策略有哪些(CacheFirst、NetworkFirst)?
【作答】:
区别:
1. 控制层面:
- HTTP缓存:浏览器自动管理,通过HTTP头控制
- SW缓存:JavaScript代码控制,更灵活
2. 缓存位置:
- HTTP缓存:浏览器缓存(内存/磁盘)
- SW缓存:Cache API,独立存储空间
3. 缓存策略:
- HTTP缓存:基于HTTP头的规则(强缓存/协商缓存)
- SW缓存:可编程策略(CacheFirst、NetworkFirst等)
4. 离线能力:
- HTTP缓存:需要网络连接验证
- SW缓存:完全离线可用
5. 更新控制:
- HTTP缓存:依赖HTTP头和浏览器行为
- SW缓存:完全由代码控制更新逻辑
6. 适用场景:
- HTTP缓存:所有HTTP请求
- SW缓存:需要离线支持、复杂缓存逻辑的场景
CacheFirst:
策略:优先使用缓存,缓存未命中时请求网络
流程:
1. 检查SW缓存
2. 命中:直接返回缓存
3. 未命中:请求网络,缓存响应后返回
适用场景:
- 静态资源(图片、字体、JS/CSS)
- 不常变化的资源
- 离线优先的场景
优点:快速响应,节省带宽
缺点:可能返回过期数据
NetworkFirst:
策略:优先请求网络,失败时使用缓存
流程:
1. 请求网络
2. 成功:更新缓存,返回响应
3. 失败:检查缓存,返回缓存或错误
适用场景:
- API请求
- 需要实时性的数据
- 网络优先的场景
优点:数据较新,有离线降级
缺点:网络慢时响应慢
StaleWhileRevalidate:
策略:立即返回缓存,同时后台更新缓存
流程:
1. 立即返回缓存(即使过期)
2. 后台请求网络更新缓存
3. 下次请求使用新缓存
适用场景:
- 需要快速响应但允许数据稍旧
- 新闻、博客等可以接受短暂延迟的内容
- 平衡性能和实时性
优点:快速响应,后台更新
缺点:首次可能返回过期数据
其他策略:
1. NetworkOnly:
- 只使用网络,不使用缓存
- 适用于必须实时获取的数据
2. CacheOnly:
- 只使用缓存,不请求网络
- 适用于完全离线的场景
3. 自定义策略:
- 根据URL、请求头等条件选择策略
- 例如:API用NetworkFirst,静态资源用CacheFirst
4. 组合策略:
- 不同资源使用不同策略
- 根据网络状态动态切换策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
6. 缓存失效与更新
如何解决缓存更新问题?版本号、文件指纹、缓存清理策略?
【作答】:
缓存更新问题:
1. 强缓存导致资源不更新:
- 用户看到旧版本
- 新功能无法使用
- Bug修复不生效
2. 协商缓存验证开销:
- 每次请求都要验证
- 增加服务器压力
3. 多级缓存同步:
- 浏览器缓存、CDN缓存、服务器缓存
- 更新不同步导致问题
4. 部分资源更新:
- 只更新了部分文件
- 新旧版本混用导致错误
版本号方案:
实现方式:
- URL参数:app.js?v=1.2.3
- 路径版本:/v1.2.3/app.js
- 文件名版本:app-v1.2.3.js
优点:
- 实现简单
- 可以手动控制版本
- 适合小项目
缺点:
- 需要手动维护版本号
- 可能忘记更新版本
- 全量更新,即使文件未变化
- 版本号冲突风险
文件指纹方案:
实现方式:
- Content Hash:基于文件内容生成hash
- app.abc123def456.js(内容变化hash变化)
- 构建工具自动生成:
- Webpack: [name].[contenthash:8].js
- Vite: 自动处理
- HTML中自动引用带hash的URL
优点:
- 自动生成,无需手动维护
- 精确:只有内容变化时hash才变化
- 支持增量更新:只更新变化的文件
- 长期缓存:URL不变时使用缓存
缺点:
- 需要构建工具支持
- 首次构建可能较慢(计算hash)
- 需要配合HTML更新机制
缓存清理:
1. 浏览器缓存清理:
- 用户手动清理(Ctrl+Shift+Delete)
- 代码无法直接清理用户浏览器缓存
2. 服务器端清理:
- 更新资源URL(hash/版本号)
- 设置较短的缓存时间
- 使用Cache-Control: no-cache
3. CDN缓存清理:
- CDN控制台手动清理
- API接口批量清理
- 设置较短的s-maxage
4. Service Worker缓存清理:
- 代码控制,可以精确清理
- 监听更新事件,清理旧缓存
- 使用Cache API的delete方法
5. 版本化清理:
- 保留多个版本,逐步清理旧版本
- 避免清理正在使用的资源
最佳实践:
1. 分层缓存策略:
- HTML: no-cache(每次验证)
- JS/CSS: 长期缓存 + hash
- 图片: 中期缓存(1-6个月)
- API: 短期缓存 + 协商缓存
2. 文件指纹 + 长期缓存:
- 使用contenthash生成文件名
- 设置public, max-age=31536000, immutable
- HTML更新时自动获取新资源
3. 渐进式更新:
- 支持多版本共存
- 逐步清理旧版本
- 避免强制更新导致的问题
4. 监控和告警:
- 监控缓存命中率
- 监控资源更新情况
- 设置异常告警
5. 降级方案:
- 缓存失效时的降级策略
- 离线时的处理方案
- 网络异常时的用户体验
6. 测试验证:
- 测试缓存更新流程
- 验证新旧版本兼容性
- 检查缓存清理是否生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
面试题(4 题)
1. 缓存策略优化
给一个网站设计完整的缓存策略,从 HTTP 缓存到 CDN 到 Service Worker,如何组合使用?
【作答】:
完整的多层缓存策略设计:
一、HTTP缓存层(浏览器缓存)
1. HTML文件:
Cache-Control: no-cache, must-revalidate
ETag: 启用
原因:入口文件,必须保证最新
2. JS/CSS文件(带hash):
Cache-Control: public, max-age=31536000, immutable
原因:内容hash确保更新,可长期缓存
3. 图片/字体:
Cache-Control: public, max-age=2592000
Last-Modified: 启用
原因:变化频率低,适合中期缓存
4. API响应:
- 用户数据:Cache-Control: private, max-age=0
- 公共数据:Cache-Control: public, max-age=300
- 实时数据:Cache-Control: no-store
二、CDN缓存层
1. 静态资源(JS/CSS/图片):
- 缓存时间:1年
- 回源验证:使用ETag/Last-Modified
- 边缘节点缓存,减少源站压力
2. HTML文件:
- 缓存时间:5分钟
- 回源验证:每次验证
- 确保及时更新
3. API:
- 根据业务决定是否CDN缓存
- 通常不缓存,直接回源
三、Service Worker缓存层
1. 静态资源策略:
- CacheFirst + 长期缓存
- 离线可用
- 后台更新
2. API策略:
- NetworkFirst + 短期缓存
- 离线降级
- 后台同步
3. HTML策略:
- NetworkFirst + 短期缓存
- 确保获取最新版本
四、组合使用方案
1. 首次访问:
- 请求 → CDN(未命中)→ 源站
- 响应设置HTTP缓存头
- SW缓存关键资源
2. 再次访问(在线):
- 检查HTTP缓存(强缓存)
- 检查SW缓存
- 检查CDN缓存
- 按优先级返回
3. 离线访问:
- SW缓存提供服务
- 显示离线提示
- 后台同步数据
4. 更新流程:
- HTML更新(no-cache)
- 新HTML引用新hash的资源
- 旧资源继续缓存(多版本共存)
- SW后台更新缓存
五、实际配置示例
Nginx配置:
location ~* \.(js|css)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header ETag on;
}
location ~* \.(jpg|png|gif|woff|woff2)$ {
add_header Cache-Control "public, max-age=2592000";
}
location = /index.html {
add_header Cache-Control "no-cache, must-revalidate";
}
Service Worker示例:
// 静态资源:CacheFirst
workbox.routing.registerRoute(
/\.(?:js|css|woff2?)$/,
new workbox.strategies.CacheFirst({
cacheName: 'static-resources',
plugins: [{
cacheableResponse: { statuses: [0, 200] },
expiration: { maxEntries: 100, maxAgeSeconds: 31536000 }
}]
})
);
// API:NetworkFirst
workbox.routing.registerRoute(
/\/api\//,
new workbox.strategies.NetworkFirst({
cacheName: 'api-cache',
plugins: [{
expiration: { maxEntries: 50, maxAgeSeconds: 300 }
}]
})
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
2. HTTP/2 优化策略变化
HTTP/2 下,传统的性能优化策略(合并文件、雪碧图、域名分片)哪些失效了?为什么?
【作答】:
HTTP/2带来的变化导致以下传统优化策略失效或不再必要:
一、失效的策略
1. 文件合并(Concatenation)
失效原因:
- HTTP/1.1:每个文件需要单独请求,合并减少请求数
- HTTP/2:多路复用,单连接并行请求,请求数不再是瓶颈
- 合并文件反而带来问题:
* 缓存粒度粗:一个文件变化,整个合并文件失效
* 并行加载受限:无法利用HTTP/2的多路复用优势
* 代码分割困难:难以按需加载
建议:
- 拆分文件,利用HTTP/2多路复用
- 使用代码分割,按需加载
- 保持文件独立性,提高缓存效率
2. 雪碧图(Sprite)
失效原因:
- HTTP/1.1:减少图片请求数
- HTTP/2:多路复用,图片请求并行,不再需要合并
- 雪碧图的问题:
* 维护困难:添加/删除图片需要重新生成
* 缓存效率低:一个图片变化,整个雪碧图失效
* 无法利用HTTP/2的并行加载优势
* 响应式适配困难
建议:
- 使用单独的图片文件
- 利用HTTP/2并行加载
- 使用WebP等现代图片格式
- 考虑使用SVG图标
3. 域名分片(Domain Sharding)
失效原因:
- HTTP/1.1:每个域名最多6个并发连接,分片增加连接数
- HTTP/2:单连接多路复用,多个域名反而增加开销
- 域名分片的问题:
* DNS查询开销:多个域名需要多次DNS查询
* 连接建立开销:每个域名需要建立连接
* 无法利用HTTP/2的单连接优势
* 可能降低性能
建议:
- 使用单一域名或少量域名
- 利用HTTP/2的单连接多路复用
- 减少DNS查询和连接建立开销
4. 内联关键资源(Inline Critical Resources)
部分失效:
- HTTP/1.1:内联关键CSS/JS减少请求
- HTTP/2:多路复用使内联的必要性降低
- 但仍有一定价值:
* 减少关键路径的RTT
* 避免阻塞渲染的资源请求
建议:
- 可以继续内联关键CSS(首屏渲染)
- JS内联的必要性降低
- 平衡内联和缓存的关系
二、仍然有效的策略
1. 资源压缩(Gzip/Brotli)
- HTTP/2头部压缩不影响内容压缩
- 继续使用Gzip/Brotli压缩资源
2. 图片优化
- 使用WebP、AVIF等现代格式
- 响应式图片(srcset)
- 懒加载
3. 代码分割
- 按路由分割
- 按需加载
- 利用HTTP/2并行加载优势
4. CDN使用
- 地理分布
- 边缘缓存
- 仍然有效
5. 缓存策略
- HTTP缓存头
- 文件hash
- 仍然重要
三、新的优化策略
1. 服务器推送(Server Push)
- HTTP/2新特性
- 主动推送关键资源
- 减少RTT
2. 资源优先级(Priority)
- 设置资源加载优先级
- 优化关键路径
3. HTTP/2特定的优化
- 利用多路复用拆分资源
- 利用头部压缩减少开销
- 利用服务器推送预加载
四、实际建议
1. 迁移策略:
- 评估当前优化策略
- 逐步移除失效策略
- 测试性能影响
2. 混合策略:
- 考虑HTTP/1.1用户(降级)
- 为HTTP/2用户优化
- 使用特性检测
3. 监控和测试:
- 监控HTTP版本分布
- A/B测试优化效果
- 持续优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
3. 缓存问题排查
用户反馈"看到的是旧版本",你如何排查缓存问题?从浏览器、CDN、服务器多个层面分析。
【作答】:
系统化的缓存问题排查流程:
一、信息收集阶段
1. 收集用户信息:
- 浏览器类型和版本
- 访问的URL
- 具体看到的内容(截图)
- 是否强制刷新(Ctrl+F5)后仍显示旧版本
- 其他用户是否也有此问题
- 问题出现时间
2. 检查服务器状态:
- 确认服务器已更新
- 检查部署日志
- 验证文件内容是否正确
二、浏览器层面排查
1. 开发者工具检查:
- Network面板:
* 查看资源状态码(200/304/from cache)
* 检查响应头(Cache-Control、ETag、Last-Modified)
* 查看资源大小和时间戳
* 确认资源URL是否正确(hash是否更新)
- Application面板:
* 检查Service Worker缓存
* 检查HTTP缓存
* 查看存储的缓存内容
2. 缓存验证:
- 硬刷新(Ctrl+Shift+R / Cmd+Shift+R)
- 清除缓存并硬性重新加载
- 无痕模式访问(排除扩展影响)
- 禁用Service Worker测试
3. 常见问题:
- 强缓存未过期:检查Cache-Control max-age
- Service Worker缓存:检查SW缓存策略
- 浏览器扩展缓存:禁用扩展测试
- 本地存储:检查localStorage/sessionStorage
三、CDN层面排查
1. CDN缓存检查:
- CDN控制台查看缓存状态
- 检查CDN缓存规则配置
- 查看CDN日志(命中/未命中)
- 检查CDN节点的缓存时间
2. CDN缓存清理:
- 手动清理特定URL
- 批量清理(按目录/文件类型)
- 检查清理是否生效
- 验证清理后的响应
3. CDN配置检查:
- 缓存时间设置(s-maxage)
- 回源验证配置
- 缓存键(Cache Key)配置
- 边缘节点同步状态
4. 多节点验证:
- 不同地区访问测试
- 检查边缘节点缓存一致性
- 验证回源策略
四、服务器层面排查
1. HTTP头检查:
- 验证Cache-Control设置
- 检查ETag生成是否正确
- 验证Last-Modified时间
- 确认响应头配置
2. 文件内容验证:
- 直接访问源站URL
- 检查文件内容是否更新
- 验证文件hash是否正确
- 检查文件时间戳
3. 服务器配置:
- Nginx/Apache缓存配置
- 反向代理缓存设置
- 应用层缓存(Redis等)
- 静态文件服务配置
4. 部署验证:
- 检查部署是否成功
- 验证文件是否覆盖
- 检查文件权限
- 确认多服务器同步
五、排查工具和方法
1. 浏览器工具:
- Chrome DevTools
- Firefox Developer Tools
- Safari Web Inspector
2. 命令行工具:
- curl -I(查看响应头)
- curl -v(详细请求信息)
- wget --spider(检查资源)
3. 在线工具:
- WebPageTest
- GTmetrix
- Pingdom
4. 日志分析:
- 服务器访问日志
- CDN日志
- 应用日志
六、问题定位流程
1. 快速验证:
1. 无痕模式访问 → 排除浏览器缓存
2. 直接访问源站 → 排除CDN缓存
3. 检查文件hash → 确认文件是否更新
4. 查看响应头 → 确认缓存配置
2. 逐步排查:
浏览器缓存 → CDN缓存 → 服务器缓存 → 源站文件
3. 验证修复:
清理缓存 → 验证响应 → 确认更新 → 监控效果
七、常见问题和解决方案
1. HTML未更新:
- 问题:HTML设置了强缓存
- 解决:改为no-cache或短时间缓存
2. JS/CSS未更新:
- 问题:文件hash未变化或缓存未清理
- 解决:确保hash更新,清理CDN缓存
3. 图片未更新:
- 问题:CDN缓存时间过长
- 解决:清理CDN缓存,调整缓存时间
4. Service Worker缓存:
- 问题:SW缓存了旧资源
- 解决:更新SW版本,清理旧缓存
5. 多级缓存不同步:
- 问题:浏览器/CDN/服务器缓存不一致
- 解决:统一缓存策略,清理所有缓存
八、预防措施
1. 合理的缓存策略:
- HTML: no-cache
- 静态资源: 长期缓存 + hash
- API: 短期缓存
2. 版本控制:
- 使用文件hash
- 确保hash唯一性
- 版本化部署
3. 监控告警:
- 监控缓存命中率
- 监控资源更新
- 设置异常告警
4. 文档和流程:
- 缓存策略文档
- 问题排查手册
- 应急处理流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
4. 缓存实践经验
讲一个你优化缓存策略的经验:初始状态、优化方案、效果数据、遇到的坑?
【作答】:
项目背景:一个中大型React SPA应用,日活10万+,用户反馈加载慢、经常看到旧版本。
一、初始状态
问题表现:
1. 性能问题:
- 首屏加载时间:3.5秒
- 重复访问仍需2秒
- 大量304请求,浪费带宽
2. 缓存问题:
- 用户经常看到旧版本
- 更新后需要强制刷新才能看到新功能
- 客服收到大量"为什么看不到更新"的反馈
3. 技术现状:
- HTML: Cache-Control: max-age=3600(1小时强缓存)
- JS/CSS: 无缓存策略,每次请求
- 图片: 无统一缓存策略
- API: 无缓存
- 未使用文件hash
- 未使用CDN
- 未使用Service Worker
4. 数据指标:
- 缓存命中率:< 10%
- 平均请求数:45个/页面
- 平均传输大小:2.1MB
- 服务器带宽:高峰期500Mbps
二、优化方案
阶段一:基础HTTP缓存优化(1周)
1. HTML缓存策略:
- 改为:Cache-Control: no-cache, must-revalidate
- 启用ETag
- 确保每次验证,但304仍可使用缓存
2. 静态资源缓存:
- JS/CSS: 设置Cache-Control: public, max-age=86400(1天)
- 图片: Cache-Control: public, max-age=604800(7天)
- 字体: Cache-Control: public, max-age=2592000(30天)
3. API缓存:
- 公共数据: Cache-Control: public, max-age=300(5分钟)
- 用户数据: Cache-Control: private, max-age=0
效果:
- 缓存命中率提升到35%
- 重复访问时间减少到1.5秒
- 但仍有缓存更新问题
阶段二:文件hash + 长期缓存(2周)
1. 构建优化:
- 配置Webpack使用contenthash
- 文件名格式:[name].[contenthash:8].[ext]
- HTML中自动引用带hash的文件
2. 缓存策略调整:
- JS/CSS: Cache-Control: public, max-age=31536000, immutable
- 长期缓存,依赖hash确保更新
3. HTML策略:
- 保持no-cache
- 确保能获取最新版本
效果:
- 缓存命中率提升到65%
- 重复访问时间减少到0.8秒
- 缓存更新问题基本解决
阶段三:CDN + 进一步优化(3周)
1. CDN接入:
- 静态资源全部走CDN
- CDN缓存时间:1年(配合hash)
- HTML缓存:5分钟
2. 资源优化:
- 图片压缩和格式优化(WebP)
- Gzip/Brotli压缩
- 代码分割优化
3. Service Worker:
- 关键资源CacheFirst
- API NetworkFirst
- 离线支持
效果:
- 缓存命中率提升到85%
- 首屏加载时间:1.8秒
- 重复访问时间:0.3秒
- 离线可用
三、效果数据
优化前后对比:
1. 性能指标:
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| 首屏加载 | 3.5s | 1.8s | 48% ↓ |
| 重复访问 | 2.0s | 0.3s | 85% ↓ |
| 缓存命中率 | 10% | 85% | 750% ↑ |
| 平均请求数 | 45 | 12 | 73% ↓ |
| 传输大小 | 2.1MB | 0.8MB | 62% ↓ |
2. 服务器指标:
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| 带宽使用 | 500Mbps | 150Mbps | 70% ↓ |
| 服务器负载 | 高 | 低 | 显著降低 |
| 304请求 | 大量 | 少量 | 大幅减少 |
3. 用户体验:
- 用户反馈加载慢的问题减少90%
- "看到旧版本"的反馈减少95%
- 用户满意度提升
4. 成本:
- CDN成本:+2000元/月
- 服务器成本:-5000元/月
- 净节省:3000元/月
四、遇到的坑
1. HTML缓存问题:
- 问题:初期HTML也设置了长期缓存,导致更新不及时
- 解决:改为no-cache,确保每次验证
- 教训:HTML必须保证能及时更新
2. 文件hash冲突:
- 问题:不同环境构建的hash不一致
- 解决:统一构建环境,使用CI/CD
- 教训:确保构建环境一致性
3. CDN缓存清理:
- 问题:紧急更新时CDN缓存未及时清理
- 解决:建立CDN缓存清理流程和工具
- 教训:需要快速清理机制
4. Service Worker更新:
- 问题:SW缓存了旧资源,更新不及时
- 解决:实现SW版本管理和缓存清理逻辑
- 教训:SW需要版本控制
5. 多版本共存:
- 问题:新旧版本资源混用导致错误
- 解决:确保HTML和资源版本匹配
- 教训:需要版本一致性检查
6. 移动端缓存:
- 问题:移动端浏览器缓存行为不同
- 解决:针对移动端调整缓存策略
- 教训:需要跨平台测试
7. API缓存误用:
- 问题:缓存了不应该缓存的用户数据
- 解决:仔细区分public/private数据
- 教训:API缓存需要谨慎设计
五、经验总结
1. 缓存策略要分层:
- 不同资源不同策略
- HTML入口必须可更新
- 静态资源长期缓存
2. 文件hash是关键:
- 解决缓存更新问题
- 支持长期缓存
- 确保版本一致性
3. 监控和测试:
- 持续监控缓存命中率
- 测试不同场景
- 建立告警机制
4. 文档和流程:
- 记录缓存策略
- 建立问题排查流程
- 团队知识共享
5. 渐进式优化:
- 分阶段实施
- 验证每阶段效果
- 及时调整策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
