浏览器缓存
Under 学习
On 2019-07-23 19:45:36

相关链接: 前端优化:浏览器缓存技术介绍 HTTP缓存控制小结

前些天,被人问到浏览器缓存,突然感觉我对浏览器好陌生。
平时,开发会勾上F12控制台的Disable cache,来避免缓存的影响,修复完一个问题,或者需求发布到开发联调环境上,总是让测试”多强刷几下“。
但是用户不是程序员,有什么方案来无感精准控制浏览器缓存?

基本概念

缓存分为硬缓存和协商缓存,浏览器是否使用缓存、缓存多久,是由服务器控制的。
准确来说,当浏览器请求一个网页(或者其他资源)时,服务器发回的响应的「响应头」部分的某些字段指明了有关缓存的关键信息。

硬缓存

浏览器在加载资源时,先根据这个资源的一些http header判断它是否命中强缓存(不会发起请求)

强缓存是通过Expires或者Cache-Control这两个http response header实现的。

cache-control: max-age=31536000
expires: Wed, 22 Jul 2020 08:50:49 GMT

Expires是较老的强缓存管理header,由于它是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如:随意修改下客户端时间,就能影响缓存命中的结果。
所以在HTTP 1.1的时候,提出了一个新的header,就是Cache-Control,这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示。

还可以为 Cache-Control 指定 public 或 private 标记。
如果使用 private,则表示该资源仅仅属于发出请求的最终用户,这将禁止中间服务器(如代理服务器)缓存此类资源。
private 并不会使得缓存更加安全,它同样会传给中间服务器。
对于 public,则允许所有服务器缓存该资源。
Cache-Control 默认设为 public 是合理的。

协商缓存

当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的另外一些http header验证这个资源是否命中协商缓存(不会响应数据)

协商缓存是利用的是(Last-Modified,If-Modified-Since)和(ETag、If-None-Match)这两对Header来管理的。

响应头:
etag: W/"9a-165d2aad640"
last-modified: Thu, 13 Sep 2018 11:22:16 GMT

请求头:
if-modified-since: Thu, 13 Sep 2018 11:22:16 GMT
if-none-match: W/"9a-165d2aad640"

请求响应返回的http状态为304并且会显示一个Not Modified的字符串

(Last-Modified,If-Modified-Since)都是根据服务器时间返回的header,一般来说,在没有调整服务器时间和篡改客户端缓存的情况下,这两个header配合起来管理协商缓存是非常可靠的,但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。
所以就有了另外一对header来管理协商缓存,这对header就是(ETag、If-None-Match)

分布式系统里多台机器间文件的Last-Modified必须保持一致,以免负载均衡到不同机器导致比对失败。
分布式系统尽量关闭掉ETag(每台机器生成的ETag都会不一样)

缓存控制

一般协商缓存都是web服务器默认开启的,时间可以通过配置修改,不过这都是固定的。
那如何在有新版本发布的时候,用户刷新就能越过缓存拿到最新的内容?如何保证前后端的变更同时生效?

最常见的做法是在构建的文件名上加上Hash或者时间戳(需要保证引用正确),还有的是在请求资源带上一个版本参数(需要服务端支持)。

思考

对应平台和应用解耦的运营平台,请求资源的路径是固定的,那如何在新版本发布的时候避免缓存?

OVER