浏览器缓存机制
沿一次真实请求梳理强缓存、协商缓存、ETag 与现代静态资源策略。
https://www.cnblogs.com/chenhuichao/p/14325953.html
这是一份基于您提供的文章所整理的浏览器缓存机制详细指南。浏览器缓存主要分为“强缓存”和“协商缓存”,合理利用它们可以显著减少数据传输、减轻服务器负担并提升网页加载速度。
浏览器缓存的基本流程
当浏览器第一次请求资源(如 HTML、CSS、JS)时,服务器会在 HTTP 的 Response Header(响应头) 中返回缓存规则。浏览器会将这些资源及其对应的缓存规则存入本地的“缓存数据库”中。再次请求该资源时,浏览器会根据规则决定是直接读取本地缓存,还是向服务器发起验证。
核心机制一:强缓存 (Strong Cache)
强缓存的逻辑非常简单粗暴:只要本地资源没有过期,就直接从本地磁盘或内存中读取,绝对不向服务器发送请求。
如何判断是否命中强缓存?
主要通过 Response Header 中的 Cache-Control 字段来判断(以前使用的 Expires 字段已基本淘汰):
max-age=xxx:(最重要)规定了资源在 xxx 秒内有效。在这段时间内再次请求,直接使用本地缓存。no-cache:(最重要)不使用强缓存,每次请求都必须直接进入下一步的“协商缓存”阶段。no-store:既不使用强缓存,也不使用协商缓存,每次都向服务器请求最新的完整资源(实际业务中较少使用)。private/public:规定是仅浏览器可以缓存,还是浏览器和代理服务器都可以缓存(前端开发一般无需深究)。
强缓存流程:
- 请求资源时,检查
Cache-Control是否有max-age。 - 如果存在且时间未过期,直接读取本地缓存(状态码通常显示 200,并提示来自 disk cache 或 memory cache)。
- 如果
max-age已过期,或者值为no-cache,则进入协商缓存阶段。
核心机制二:协商缓存 (Negotiated Cache)
当强缓存失效(max-age 过期)或被明确禁用(no-cache)时,协商缓存就会触发。此时,浏览器必须向服务器发送一次 HTTP 请求,来“协商”本地的旧缓存是否还能继续使用。
协商的依据(两对标识):
服务器在初次返回资源时,会在 Response Header 中带上以下两个标识:
ETag:文件的唯一标识符(类似于文件的 MD5 值,内容一变它就变)。Last-Modified:文件的最后修改时间。
协商缓存流程:
浏览器再次请求资源时,会在 Request Header 中将之前保存的标识带给服务器,但字段名会发生变化:
ETag变成If-None-MatchLast-Modified变成If-Modified-Since
服务器接收到这两个字段后,与服务器上最新资源的标识进行对比。
结果 A(资源未更改):服务器返回状态码
304 Not Modified。因为资源没变,所以此时不返回 Body 数据,响应体积极小,浏览器直接去本地读取旧缓存。结果 B(资源已更改):服务器返回状态码
200 OK,并在 Body 中带回最新的资源数据和最新的ETag/Last-Modified供浏览器下次缓存。
深入细节:为什么有了 Last-Modified 还需要 ETag?
HTTP 1.1 引入 ETag 主要是为了弥补 Last-Modified 的几个致命缺陷:
- 修改频繁度不够:
Last-Modified的时间粒度只能精确到秒。如果一个文件在 1 秒内被修改了多次,时间戳察觉不到,会导致浏览器使用错误的老缓存。 - 内容没变但时间变了:有时文件只是被重新生成,内容毫无变化,但修改时间更新了。此时如果不看
ETag,服务器会误以为资源更新了,从而返回完整的 200 数据,造成带宽浪费。 - 时间获取不精确:某些服务器无法精确获取文件的最后修改时间。
用户行为对缓存的干预
用户在浏览器中的刷新操作,会直接影响缓存策略的执行:
- 普通刷新 (F5):跳过强缓存规则(直接视为过期),强制向服务器发起协商缓存验证。
- 强制刷新 (Ctrl+F5):跳过所有缓存规则(强缓存和协商缓存全都不走),表现得就像第一次访问一样,重新从服务器获取所有完整资源。
现代项目的最佳缓存策略(以 Vue 等 SPA 单页应用为例)
在现代前端工程化项目中(如 Vue/React),代码打包工具(如 Webpack/Vite)通常会为静态资源(JS、CSS、图片)的的文件名加上哈希值(如 app.3f8a9b.js)。
基于这种特性,最佳实践如下:
- 静态资源(JS/CSS/图片):因为内容一旦改变,文件名(hash)就会变,因此可以给这些文件设置长达一年的强缓存 (
Cache-Control: max-age=31536000)。 - 入口文件(
index.html):这是整个应用的入口,内部引入了带有 hash 的 JS/CSS。如果 HTML 被缓存,用户就会一直访问旧版本的资源链接。因此,需要在 Nginx 或服务器层面,将index.html配置为Cache-Control: no-store或no-cache,确保用户每次都能拉取到最新的入口文件。