浏览器缓存机制
基本流程
- 加载资源时,首先根据上次资源响应 Header 中的
Expires
和Cache-Control
判断是否命中强缓存,如果命中则直接从缓存中取,不会发送资源请求到服务器 - 如果没有命中强缓存,则服务端根据
Last-Modified/If-Modified-Since
和ETag/If-None-Match
验证是否命中协商缓存,如果命中则返回 304 响应,不会返回资源数据 - 如果没有命中协商缓存,则服务端返回资源数据
比较
相同点:不论是哪种缓存,都是从本地加载资源,服务端不会返回资源数据
不同点:强缓存不会产生请求,协商缓存会产生请求
强缓存
Expires
表示缓存到期时间,是一个绝对时间,形如Thu, 10 Oct 2017 01:47:03 GMT
,它存在于资源的响应头中。该字段出现于 HTTP/1.0 规范中。Cache-Control
表示缓存到期的相对时间,以下是一些常用字段值
- max-age=86400,表示 24 小时后失效
- must-revalidate,如果超过了 max-age 设置的时间,必须发送请求验证资源是否还有效
- no-cache,依然缓存资源,但是否使用该资源,由后续的对比决定
- no-store,真正意义上的“不要缓存”
- public,内容可以被客户端和代理缓存(如 CDN)
- private,内容只能被客户端缓存,代理不能缓存,默认值。
这些字段可以被混合使用,字段间关系如下
Cache-Control 的优先级高于 Expires,出于兼容性考虑,两个字段常常会被同时设置
协商缓存
如果没有强缓存相关字段,或者强缓存判定资源失效时,进入协商缓存阶段机制(也就是说,强缓存优先级高于协商缓存)。
Last-Modified & If-Modified-Since
第一次请求资源时,资源响应头通过Last-Modified
字段告知客户端资源的修改时间,客户端会记录下该时间,在下次请求时,会将该时间写入资源请求头的If-Modified-Since
字段。服务端收到该请求后,将If-Modified-Since
中的时间与资源的上次修改时间进行对比,两个值一致时,返回 304 响应,否则正常返回新的资源。
ETag & If-None-Match
该机制的运行流程和Last-Modified & If-Modified-Since
类似,只不过将资源修改时间改成了资源内容的 hash,该 hash 在资源响应头的 ETag 中体现,在之后的资源请求头的 If-None-Match 中传回,服务器进行对比,如果一致则返回 304,否则正常返回资源。
ETag 的优先级要高于 Last-Modified
Memory Cache vs Disk Cache
Chrome DevTools 有时会观察到(from memory cache)
,有时则是(from disk cache)
,可以通过以下操作复现
- 资源设置为
Cache-Control: max-age=86400
,首次打开页面时,响应 200,返回资源 - 刷新页面,显示 from memory cache
- 关闭页面,重新打开,显示 from disk cache
此外,还可以观察到一个现象,from memory cache 时,耗时很短,往往是 0ms;而 from disk cache 时,往往需要消耗几毫秒的时间。说明确实是前者从内存里读,后者从硬盘里读。
no-cache vs no-store
用下面的场景进行说明。假设在 HTML 中,同步地请求同一个资源两次(按顺序写两遍),之后异步地再请求一次。
- 当资源被设置为 no-cache 时,该资源只被请求了一次,说明 no-cache 的语义实际上是下次打开页面时,要重新请求资源,但在当前页面上的请求,有缓存的时候还是可以放心用的
- 资源被设置为 no-store 时,该资源被请求三次,说明 no-store 无论无何都会发送请求(猜测,和名字体现出来的一样,no-store 根本就没把资源存储下来)