浏览器的缓存分两种情况:强缓存和协商缓存。
强缓存
在发送 http 请求下载资源之前首先检查强缓存。使用的字段在 http/1.0 和 http/1.1 中分别是 Expires 和 Cache-Control。
Expires 即过期时间,存在于服务器返回的响应头中,浏览器在这个过期时间前再次请求同一资源时将直接从缓存里面获取数据,无需再次发送 http 请求。事例如下:
Expires: Wed, 21 Oct 2020 07:28:00 GMT
使用此字段的缺陷是服务器的时间和浏览器的时间可能不一致,这样服务器返回的过期时间就不一定是准确的。因此这种方式在 http/1.1 中被废弃。
http/1.1 中使用的字段是 Cache-Control, 也存在于服务器返回的响应头中,但采用过期时长而非具体过期时间点,示例如下:
Cache-Control:public, max-age=86400
表示在响应返回后的 86400 秒也就是 24 小时之内可以直接使用缓存。
public 是另一个缓存指令,表示响应可以被任何对象(包括发送请求的客户端、代理服务器等等)缓存,即使是通常不可缓存的内容。此外,还可组合如下的缓存指令:
private: 表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。
no-cache: 跳过当前的强缓存检查,发送 http 请求,即直接进入 协商缓存阶段。
no-store: 缓存不应存储有关客户端请求或服务器响应的任何内容,即不使用任何缓存。
注意:当 Expires 和 Cache-Control 同时存在的时候,Cache-Control 会被优先考虑。
协商缓存
当强缓存失效,浏览器在 http 请求头中加入某些字段,服务端根据这些字段确定浏览器是否能够使用缓存,这就是协商缓存。这样的字段有两对:Last-Modified/If-Modified-Since 和 ETag/If-None-Match。
浏览器第一次给服务端发送请求后,服务器会在响应头中加上 Last-Modified 这个字段和值。浏览器接收到这个字段并在第二次给服务端发送请求时用 If-Modified-Since 字段携带该值。服务端接收到后会和服务端中该资源的最后修改时间作对比,如果请求头中的这个值小于最后修改时间,则返回新的资源,否则返回304,告诉浏览器直接用缓存。
另一对,ETag 是服务器根据当前文件的内容,给文件生成的唯一标识,一旦文件内容有改动,这个值就会变化。服务器通过响应头把这个值传给浏览器,浏览器接收到ETag的值,会在下次请求时将这个值放到请求头 If-None-Match 字段中,然后发给服务器。 服务端接收到 If-None-Match 后,会跟服务器上该资源的 ETag 进行比对,如果两者不一样,返回新的资源,否则返回304,告诉浏览器直接用缓存。
另外,在 ETag 和 If-Match 请求头的帮助下,可以检测到"空中碰撞"的编辑冲突。
比较这两种方式,精确度上 ETag 要优于 Last-Modified。因为 Last-Modified 的最小单位是秒,假如文件在一秒内改变了多次,这个时候 Last-Modified 的值并没有变化。
性能上 Last-Modified 要优于 ETag,因为 Last-Modified 仅仅只是记录一个时间点,而 Etag 需要根据文件的具体内容生成哈希值。
————————
文章已同步至公众号 《前端做题家》,关注回复“资料”领取前端面试题和学习资料。
文章均为个人理解,仅供参考,欢迎留言讨论交流。