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」 。实际工作中,工程师总是会同时使用它们。

最后说一句

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

猜你喜欢

转载自juejin.im/post/7034522650886176782