浏览器的强缓存和协商缓存

2,103 阅读5分钟

为啥要使用缓存:

缓存的优点:

  1. 加快浏览器加载网页的速度,优化用户体验,让用户更快的打开我们的网页;
  2. 减少对服务器的访问次数,减轻服务器的负担;
  3. 节省带宽(就是节省钱...,因为很多带宽服务其实是按浏览量来计费的;对用户来说也可以省不少4G、5G流量...也省钱了!双赢!!!)

有啥缺点:

如果资源一直被缓存了,那当资源发生更改时,用户就无法获取最新的信息了!所以缓存虽好,可不要“贪杯”哦!

都说缓存分为强缓存和协商缓存,今天就来讲一讲它们都是个啥?

强缓存

从名字就能发现,有点“强制”的意思;也就是只要强缓存还在,你的资源变化了我也不取用,这...那要它何用?

所谓存在即合理,它总有它的适用场景,那到底该怎么用呢?

强缓存利用的是http头中的ExpiresCache-Control 这两个字段来设置的。

Expires

从图中可以看到,它的值是一个绝对时间,这个时间代表资源的失效时间,就是说在这个时间之前缓存始终有效,始终会读取缓存中的数据。

然而,这里会有一个明显的问题:因为它是一个绝对时间,当服务器时间与客户端时间有偏差时,就可能会导致缓存失效,比如用户发现了本地机器可以随意修改时间时,那这个缓存就会GG了!

Cacha-Control

http1.1加入了Cache-Control,上图中可以看到它的值:max-age=2592001,表示资源的最大有效时间的"秒数",是一个相对值,不会因为用户修改本地时间而导致失效。

除了max-age之外,还有一些其他的值:

  1. cache-control: max-age=xxxx, public public 表示客户端和代理服务器(如CDN)都可以缓存;

  2. cache-control: max-age=xxxx, private private(默认值)表示只有客户端可以缓存;

  3. cache-control: max-age=xxxx, immutable immutable 客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,即使用户做了刷新操作,也不向服务器发起http请求; immutable是facebook团队向制定 HTTP 标准的 IETF 工作组提到的建议:他们希望 HTTP 协议能给 Cache-Control 响应头增加一个属性字段表明该资源永不过期,浏览器就没必要再为这些资源发送条件请求了。 这里有一篇文章演示了immutable

  4. cache-control: no-cache no-cache客户端缓存内容,但是是否缓存需要经过协商缓存来校验决定;

  5. cache-control: no-store no-store所有内容都不会被缓存,即不用强缓存,也不使用协商缓存(后面会讲);

PS: ExpiresCache-Control可以在服务端配置为同时启用,同时启用时Cache-Control优先级更高。

协商缓存:

  协商缓存就是强制缓存失效后,或者强缓存设置了cache-control: no-cache,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程:

  协商缓存由两对值来设置的: Last-Modified/If-Modified-SinceETag/If-None-Match

Last-Modified/If-Modified-Since:

  1. 浏览器第一次发送请求,让服务端在response header中返回请求的资源上次更新时间(Last-Modified的值),浏览器会存下这个时间;

  2. 当浏览器下次请求时,request header中带上If-Modified-Since(即保存的Last-Modified的值)。根据浏览器发送的修改时间和服务端的wh修改时间进行比对,一致的话代码资源没有改变,服务端返回正文为空的响应,让浏览器在缓存中读取资源,从而减少请求消耗。

缺点:

  1. 从上图可以看到Last-Modified保存的是绝对时间,并且是精确到秒,所以如果资源在1秒内修改了多次的话,那就无法识别;

  2. 对于文件只改变了修改时间,内容不变,这时候也会使缓存失效,其实这个时候我们是不希望客户端重新请求的;

  3. 某些服务器不能精确的得到文件的最后修改时间;

ETag/If-None-Match:

  1. 浏览器第一次发送一个请求得到ETag的值,然后在下一次请求request header中带上If-none-match(即保存的ETag的值);

  2. 通过发送的ETag的值和服务器重新生成的ETag的值进行比对,如果一致代表资源没有改变,服务器返回的正文为空的响应,让浏览器从缓存中读取资源,从而减少请求消耗。

  3. ETag的工作机制跟Last-Modified基本一样。但是,ETag是对资源内容使用抗碰撞散列函数(我也不知道是啥),使用最近修改的时间戳的哈希值。ETag解决了Last-Modified上述问题。

缺点:ETag虽然能解决问题,但也并非完美,ETag每次服务端生成都需要进行读写操作(因为要生成hash),而Last-Modified只需要读取操作,ETag消耗更大一些。

Last-ModifiedETag同样可以同时配置,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。

精确度上,ETag更高,性能上Etag稍微低一点(因为要生成hash)

由此可见,协商缓存其实受强缓存的影响,强缓存过期了且Cache-Control不为no-store时是否缓存才由协商缓存决定。