快速上手 HTTP Cache
2025-01-13
开始系统研究 HTTP Cache,是因为我在开发 裁员追踪器 | Layoffs Tracker🔗 遇到的一个问题:
API 返回的接口数据是静态的,很长时间都不会更新,如果每次都需要访问服务器,一来效率低下,二来会对服务器造成不必要的压力。
所以考虑加一层 Cache。
最简单的实现方式,就是 HTTP Cache。
本来以为,只是加几个 HTTP Header 的事情,后来研究一番后发现,这一块的内容,远比想象中丰富。
所以萌发出,写一篇文章,体系化梳理 HTTP Cache,重点将放在:产生的背景,解决的手段,以及最佳实践。
1. 背景
Cache(缓存)在整个计算机系统里被广泛使用。
从最底层的 CPU Cache,到 DNS 解析,CDN,再到耳熟能详的 Redis,无一不是这个概念的应用。
用少量空间,将耗时的计算提前预存,使用的时候直接获取。
内在原理,是局部性(Locality),这其中又分为时间局部性和空间局部性。
意思是,短期内访问的内存,在未来更有可能被访问;附近的内存,同样存在被更大概率访问的可能。
HTTP Cache 也是如此。
不仅可以加快网页的加载速度,优化用户体验,同时帮助减轻服务器负载。
举个例子。
如果一个静态文件,长期不被修改,那么每次刷新网页时都重新请求是不必要的。一来速度慢,二来增加网络访问开销,如果文件尺寸较大,又会给服务器带来一定压力。
HTTP Cache 可以很好地解决这个问题。第一次访问时,缓存在本地,第二次访问便可避开网络请求,直接从本地获取。
2. 解决方案
HTTP Cache 主要有两种:
- 强制缓存,客户端直接使用本地缓存
- 协商缓存,客户端向服务器发送请求,由服务器决定是否使用缓存
2.1 强制缓存
强制缓存的关键 header 有两个:
Expires
(不推荐),指定过期的时间点,是一个绝对时间,例如:Expires: Wed, 15 Jan 2025 20:00:00 GMT
Cache-Control
(推荐),一种更加精细,且更通用的控制手段。常用指令:public
: 表示资源可以被任何缓存共享,例如代理服务器private
: 表示资源仅限于用户终端缓存(私有),代理服务器不可以no-cache
: 每次请求必须向服务器请求验证no-store
: 表示完全不允许缓存max-age=3600
: 表示资源有效期为 3600 秒s-maxage=3600
:覆盖max-age
和Expires
,但仅用于共享缓存,私有缓存会忽略
我在 裁员追踪器 | Layoffs Tracker🔗 用到的配置如下:
缓存的时间为 60 秒,且可以被共享。
2.2 协商缓存
相比强制缓存这种单边决定,协商缓存更加复杂:需要客户端和服务端的双向配合。
关键头部有两部分,请求头和响应头。
- 请求头:
If-Modified-Since
: 上次修改时间,用于配合Last-Modified
If-None-Match
: 本地的缓存版本标识(ETag)
- 响应头:
Last-Modified
,最后一次修改的时间,可用于快速比较资源是否发生变化,但精度不如ETag
ETag
,资源的唯一标识,如果原始资源变更,ETag
必须重新生成,通过比较ETag
可以高效、准确地判断是否有变化发生
具体的工作流程是:
- 客户端在第一次请求资源时,会收到
Last-Modified
或ETag
信息 - 客户端再次请求资源,附带
If-Modified-Since
或If-None-Match
字段 - 服务端根据请求字段,判断缓存是否有效,
- 如果有效,返回
304 Not Modified
,客户端直接使用缓存 - 如果无效,返回新的资源及其状态码
200 OK
- 如果有效,返回
类似上面的例子,访问某个 .js
资源:
2.3 优先级
强制缓存和协商缓存可以同时存在。
同时存在时,会优先判断强制缓存是否有效,无效则进一步使用协商缓存。
当然,如果 Cache-Control: no-store
存在,缓存功能被禁用。
3. 最佳实践
不同的场景适合不同的缓存策略。
对于静态文件,如 CSS、JS 和图像等,可以通过设置较长的缓存时间,同时将版本信息填充到文件名里,避免因缓存时间过久导致未能及时获取最新数据。例如:
Cache-Control: max-age=31536000
- 文件名示例:
style.v2.css
、app.123abc.js
对于动态资源而言,变动频率较高,可以利用协商缓存:
ETag
和Last-Modified
提供内容变更验证机制- 响应头设置
Cache-Control: no-cache
对于涉及登陆等用户隐私相关的,建议禁止缓存:
- 响应头设置
Cache-Control: no-store, private
4. 总结
在优化性能方面,缓存是个简单而强大的工具。
以空间换时间。在准确性和效率中间寻找折衷。
组合使用 HTTP Cache 提供的功能,优化加载和网络访问,差不多是前端的必修课。
毕竟,在复杂多变的网络环境中,缓存多少提供了一些确定性。
这个世界很公平。任何选择,都会带来一些结果,以及代价。
(完)
参考
- 本文作者:Plantree
- 本文链接:https://plantree.me/blog/2025/http-cache/
- 版权声明:所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
最后更新于: 2025-01-15T05:49:17+08:00