Appearance
目录
核心思想
网络请求是昂贵的,它消耗时间和带宽。最高效的请求就是不发送请求。HTTP 缓存机制是浏览器的一种"记忆能力",它让浏览器可以"记住"已经获取过的资源。当下次需要同一个资源时,浏览器可以根据一套规则,决定是从本地"记忆"中直接读取,还是需要去询问服务器资源是否更新。这就是减少请求的艺术。
类别一:强缓存
强缓存是最高效的方式。如果一个资源命中了强缓存,浏览器会直接从本地副本加载,根本不会向服务器发送任何请求。这在开发者工具的 Network 面板中,通常表现为 200 OK (from memory cache) 或 200 OK (from disk cache)。
1. 工作原理
当浏览器第一次请求一个资源时,服务器在返回资源的同时,会通过响应头 Cache-Control 告诉浏览器这个资源的"过期时间"。
2. 核心响应头:Cache-Control
这是 HTTP/1.1 中用于控制缓存的核心字段,它有几个关键指令:
max-age=<seconds>: 最重要的指令。指定资源在被视为"过期"之前,可以被缓存的最大时间(秒)。例如,max-age=31536000表示缓存一年。publicvsprivate:public: 表明响应可以被任何中间人(如 CDN、代理服务器)缓存。private: 表明响应是针对单个用户的,只能被最终用户的浏览器缓存。
no-cache: 注意!不是"不缓存"的意思。它的意思是"不要使用强缓存,但在使用缓存副本前,必须去服务器验证一下它是否过期"(即,强制进入协商缓存阶段)。no-store: 这才是"完全不缓存"。浏览器和任何中间缓存都不会存储这个响应的任何部分。常用于涉及敏感信息的请求。
类别二:协商缓存
当资源的强缓存"通行证"过期了,或者压根就没有设置强缓存(例如设置了 Cache-Control: no-cache),浏览器就必须向服务器发送一次请求来"协商"一下。浏览器会问:"我本地存的这个版本还能用吗?"
1. 工作原理
协商缓存需要浏览器和服务器之间的一次通信。浏览器在请求头中携带本地副本的"版本信息",服务器根据这个信息判断资源是否已更新。
- 如果未更新:服务器返回一个
304 Not Modified状态码,响应体为空。浏览器收到后,便安心地从本地加载缓存副本。这个过程虽然有一次请求,但由于没有下载资源本身,所以速度很快,极大地节省了带宽。 - 如果已更新:服务器返回
200 OK状态码,并在响应体中携带全新的资源内容,同时更新"版本信息"。
核对购物小票
- 保留小票:你(浏览器)买了一件衣服(资源),并保留了购物小票。小票上有两个重要信息:购买时间 和 商品唯一码。
- 拿着小票去询问:过了几天,你想知道这件衣服有没有出新款。你拿着小票去问店员(服务器):"我是在这个时间点买的、商品码是这个的衣服,现在还是最新的吗?"(发送带有 If-Modified-Since 和 If-None-Match 的请求)。
- 店员的回应:
- 未出新款:店员看了一眼说:"没变,还是这个,你那个就是最新的。"(返回 304)。你便放心地继续穿旧衣服。
- 已出新款:店员说:"哦,我们出新款了,你那个过时了。" 然后直接拿给你一件新款的衣服和一张新的小票。(返回 200 和新资源、新 ETag)。
2. 核心"版本信息"头
协商缓存依赖于两组配对使用的头部字段:
- 第一组:基于时间戳
Last-Modified(服务器响应头): 资源在服务器上最后被修改的时间。If-Modified-Since(浏览器请求头): 当浏览器发起协商请求时,会带上这个头,其值就是上次收到的 Last-Modified 的值。
- 第二组:基于唯一标识
ETag(Entity Tag, 服务器响应头): 服务器为当前版本的资源生成的唯一标识符,像一个"指纹"。只要资源内容有变动,ETag 就会改变。If-None-Match(浏览器请求头): 浏览器发起协商请求时,带上这个头,其值就是上次收到的 ETag 的值。
为什么 ETag 通常优于 Last-Modified?
ETag 更精确。Last-Modified 只能精确到秒,如果在 1 秒内文件被多次修改,Last-Modified 无法感知。此外,有时文件内容没有变,只是保存了一下,修改时间也会变,这会导致不必要的重新下载。ETag 基于文件内容生成,完美避开了这些问题。因此,ETag 的优先级更高。
总结:缓存决策流程
当浏览器需要一个资源时,它会按照以下顺序决策:
- 检查强缓存:查看本地副本的
Cache-Control: max-age是否仍然有效。
- 是 -> 命中强缓存。直接从本地加载,不发请求。 (最快)
- 否 -> 进入协商缓存阶段。
- 发起协商缓存请求:向服务器发送一个请求,携带
If-Modified-Since和/或If-None-Match头。
- 服务器判断资源未改变 -> 返回
304 Not Modified。浏览器从本地加载。(较快) - 服务器判断资源已改变 -> 返回
200 OK和新资源。浏览器使用新资源并更新本地缓存。(最慢,但必要)
| 特性 | 强缓存 | 协商缓存 |
|---|---|---|
| 是否联系服务器 | 否 | 是 |
| HTTP状态码 | 200 OK (from cache) | 304 Not Modified 或 200 OK |
| 服务器响应 | 无 | 304时响应体为空,200时为新资源 |
| 核心请求头 | (无) | If-Modified-Since, If-None-Match |
| 核心响应头 | Cache-Control | ETag, Last-Modified |
| 性能 | 极高,无网络延迟 | 较高,有网络延迟但节省带宽 |
| 适用场景 | 长期不变的静态资源 (JS, CSS, 图片) | 可能会频繁更新的资源,或需要保证新鲜度的资源 |