web性能优化技巧篇之 HTTP 缓存一

这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

HTTP 响应可以要求浏览器将文件缓存一段时间,具体写法为: Cache-Control: public, max-age=3600, must-revalidate

max-age

其中 max-age: 3600 表示最长缓存时间为 3600 秒,public 表示网络中的中间设备(如代理)也可以缓存此内容,must-revalidate 表示缓存过期后不能再使用,必须重新校验。

在未来的 3600 秒内,浏览器对于相同 URL 的请求,一律不发出,且直接使用缓存作为其响应:

image.png

图中的 (memory cache) 表示响应是从内存里读取的(有的时候会从硬盘中读取)。

校验(validate)

假设我们使用 Cache-Control: max-age=3600 让一个 JS 文件被浏览器缓存一小时,那么在一小时内,即使用户刷新页面,浏览器也不会再去请求相同 URL 的 JS 了。但是在一小时后呢?如果此时用户刷新页面,页面请求了相同 URL 的 JS 文件,浏览器应该怎么做?有三种可能的选择:

  1. 直接发起一个全新的请求(重新下载一次 JS 文件,其内容可能跟缓存一样,也可能不一样)。
  2. 问服务器当前的缓存是否可以继续使用(如果缓存还能用,就没必要再下载一次),这就是校验。
  3. 使用过期的缓存内容(比如断网了,过期内容总比什么都没有强)。

综合比较,发现校验最划算,must-revalidate 就是要求浏览器必须校验。

但是验证有一个前提,必须得有一个验证凭据,总不能把整个 JS 文件内容上传给服务器问这个内容有没有变吧。

程序员很容易想到了哈希(Hash),只要用某一种哈希算法把内容映射为一个短小的字符串,通过对比这个短小的字符串就能知道内容变没变了呀。

因此,常见的验证过程是这样的:

  1. 浏览器第一次访问 JS 时,服务器除了添加缓存之外,还要算出 JS 文件的哈希值,附加在响应头里,写法是 Etag: abcd1234。(Etag 是实体标签的意思)
  2. 在缓存有效期内,浏览器不会再对相同的 URL 发出请求。
  3. 等缓存有效期结束后,浏览器再次请求该 JS 文件,但是会在请求头里附上 If-None-Match: abcd1234
  4. 假设服务器发现 JS 文件的哈希值为 abcd1234,跟浏览器附上的哈希值是一样的,说明文件没变,就会返回 304 Not Modified
  5. 假设服务器发现 JS 文件的哈希值变了,就会返回 200,并在消息体里写上最新的文件内容。

有人把这种服务器和浏览器商量是否能缓存的方案叫做 「协商缓存」 ,把有效期内的缓存叫做 「强缓存」 。但这两个词属于生造词,对应的术语是 「内容协商 Content Negotiation」「缓存 Caching」 。实际工作中,工程师总是会同时使用它们。

最后说一句

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。

Guess you like

Origin juejin.im/post/7034522650886176782