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

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

HTTP版本升级

你可能还听过 Expire 响应头可以用于强缓存,Last-Modified 响应头配合 If-Modified-Since 请求头可以用于协商缓存。为什么会有两套类似的方案呢?看看它们对应的 HTTP 版本就知道了:

  • HTTP/1.0 引入了 ExpireLast-ModifiedIf-Modified-Since
  • HTTP/1.1 引入了 Cache-ControlEtagIf-None-Match

所以,这其实是一次升级,HTTP/1.1 引入的方案解决了之前的方案的一些问题:

  1. Expire 指定缓存过期时间为某个具体的时刻,但如果用户浏览器的时间是错的,那么缓存时间就不符合预期了。Cache-Control 则改为指定缓存时长来规避这个问题。
  2. Last-Modified 用修改时间来表示文件的版本,但如果一秒内修改两次,就无法区分版本了。Etag 则改为用哈希来描述文件的版本来规避这个问题。
  3. If-Modified-Since 是浏览器让服务器对比两个文件的修改时间,也存在上面的问题。If-None-Match 则是让服务器对比两个文件的哈希来规避这个问题。

实际工作中,程序员可能会同时使用这两套方案,以兼容一些旧的网络设备和浏览器。

缓存文件

即使没有 Cache-Control 响应头,当响应的状态码为 200、203、206、300、301、410 时,网络设备或浏览器可能会将其缓存起来。

一般来说,静态文件、get请求得到的 JSON 响应,都可以被缓存起来。

如果你不希望一个响应被缓存起来,可以在响应头里写: Cache-Control: max-age=0

这可以让当前内容立即过期(如果在这之前还有更早地缓存,同样立即过期)。浏览器对于这样的内容:

  1. 可以将其保存起来,用于内容协商(俗称协商缓存),即虽然不缓存它,但是在发请求向服务器验证得到 304 状态码后,依然可以复用它。
  2. 也可以在下一次相同 URL 的请求出现时,直接使用过期的缓存作为响应。

如果你不想要浏览器使用过期的缓存,你可以在响应头里这样写: Cache-Control: max-age=0, must-revalidate 这样一来,该响应会立即过期,但过期的缓存可以用于内容协商。

如果你不希望响应被保存起来用于内容协商,可以在响应头里写 Cache-Control: no-store。那么本次响应就既不会被缓存,也不会被保存起来用于内容协商。 但这样写并不会使以前存在的缓存失效,所以一般会和 max-age=0 一起使用: Cache-Control: no-store, max-age=0

实际中的缓存用法

  • index.html 不能缓存,响应头里加 Cache-Control: max-age=0 防止缓存,加 Etag 用于内容协商,加 Expire: 过去的时间 用于兼容旧浏览器。
  • CSS、JS 和图片文件一律在文件名后面加版本字符串,如 index-855fcfd82e.js,响应头里加 Cache-Control: max-age=2592000 缓存一个月以上,Etag 可以不加(因为内容不会变,过时的缓存可以用)。CDN 服务商一般都会帮你做这些事。
  • 当 CSS、JS和图片文件内容有变化时,直接修改文件名中的版本字符串(Webpack 会帮你做),替换 index.html 里的旧文件名即可。旧文件的缓存会一直躺在用户的电脑里直到过期后被浏览器自动清理。
  • API 响应头里加 Cache-Control: no-store, max-age=0 防止缓存和内容协商。 另外,Cache-Control 既可以出现在请求头里,也可以出现在响应头里。以上说的 Cache-Control 都是指响应头,而不是请求头,出现在请求头中含义会有所不同,暂时不讨论。

最后说一句

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

猜你喜欢

转载自juejin.im/post/7034862669504970760