强制缓存和协商缓存

一、缓存

缓存的优点:
  • 减少了不必要的数据传输,节省带宽
  • 减少服务器的负担,提升网站性能
  • 加快了客户端加载网页的速度
  • 用户体验友好
缓存的缺点:
  • 资源如果有更改但是客户端不及时更新会造成用户不能及时获取信息,如果老版本有bug的话,情况会更加糟糕。
缓存类型

就浏览器而言,一般缓存我们分为四类,按浏览器读取优先级顺序依次为:Memory CacheService Worker CacheHTTP CachePush Cache。而本篇文章主要讲的就是 HTTP Cache

二、HTTP Cache

HTTP Cache 是我们开发中接触最多的缓存,分为强缓存和协商缓存。
强缓存:浏览器第一次请求时直接缓存资源,缓存本地,第二次查看资源是否过期,没过期则直接使用资源,过期则重新请求。
协商缓存:浏览器第一次请求缓存且保存缓存的标识和时间,重复请求,浏览器会去请求服务器验证资源是否更新,服务器根据缓存标识进行判断,没有失效则使用缓存,此时返回的是 304,通知客户端可以使用缓存数据。如果失效则重新发送资源。

一般做了强缓存,只有在强缓存失效了才走协商缓存。

缓存 状态码 请求务器
强缓存 200(from memory cache) 否,直接从缓存取
协商缓存 304 是,通过服务器告知浏览器缓存是否可用

三、强缓存

强缓存来说,响应header中会有两个字段来标明失效规则(Expires/Cache-Control)

1.Expires

Expires的值为服务端返回的到期时间,即下一次请求时,请求时间小于服务端返回的到期时间,直接使用缓存数据。
Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。

2.Cache-Control

取值有privatepublicno-cachemax-ageno-store

取值 作用
private 客户端可以缓存 ,代理服务不缓存
public 客户端和代理服务器都可缓存
max-age=xxx 缓存的内容将在 xxx 秒后失效
no-cache 跳过设置强缓存,但是不妨碍设置协商缓存
no-store 不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了
immutable 如果请求该资源就直接读取缓存,即使用户做了刷新操作,也不向服务器发请求

强制缓存和协商缓存

3.pragma

值有no-cacheno-store,表示意思同 cache-control,优先级高于 cache-control 和 expires,即三者同时出现时,先看 pragma > cache-control> expires。

四、协商缓存

协商缓存来说,响应header中会有两个字段来标明(Last-Modified / ETag)

1.Last-Modified

Last-Modified的值标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
强制缓存和协商缓存
当再次请求该资源时,请求头中会带有 If-Modified-Since 字段,值是之前返回的 Last-Modified 的值,如:If-Modified-Since: Mon, 06 Jul 2020 06:27:10 GMT。服务端会对比该字段和资源的最后修改时间,若一致则证明没有被修改,告知浏览器可直接使用缓存并返回 304;若不一致则直接返回修改后的资源,并修改 Last-Modified 为新的值。

缺点

  • 只要编辑了,不管内容是否真的有改变,都会以这最后修改的时间作为判断依据,当成新资源返回,从而导致了没必要的请求响应,而这正是缓存本来的作用即避免没必要的请求。
  • If-Modified-Since时间的精确度只能到秒,某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),这种修改无法判断。
2.ETag

为了解决 Last-Modified 上述问题,有了 ETag
每个文件有一个ETag,改动文件了就变了,就是个文件hash,每个文件唯一,就像用webpack打包的时候,每个资源都会有这个东西,如: app.js打包后变为 built.85d2fbbf33.js,加个唯一hash,也是为了解决缓存问题。[webpack中hash][2]
强制缓存和协商缓存
协商缓存的过程

  • 发请求–>看资源是否过期–>过期–>请求服务器–>服务器对比资源是否真的过期–>没过期–>返回304状态码–>客户端用缓存的老资源。

  • 发请求–>看资源是否过期–>过期–>请求服务器–>服务器对比资源是否真的过期–>过期–>返回200状态码–>客户端如第一次接收该资源一样,记下它的cache-control中的max-age、etag、last-modified等。

请求资源时,把用户本地该资源的 etag 同时带到服务端,服务端和最新资源做对比。
如果资源没更改,返回304,浏览器读取本地缓存。
如果资源有更改,返回200,返回最新的资源。

五、访问刷新分析

访问和刷新分为以下三种情况:

  • 标签进入、输入url回车进入
  • 按刷新按钮、F5 刷新、网页右键“重新加载
  • ctrl + F5 强制刷新
1.标签进入、输入url回车进入

这种情况下会根据实际设计的缓存策略去判断。

  • 如果先走强缓存路线。根据 cache-control (expires 优先级低)判断缓存是否过期,若没有过期则此时返回 200(from cache)。
  • 若本地缓存已经过期再走协商缓存路线,根据之前的 last-modified 值去与服务器比对
    • 若这个时间之后没有改过则去读取本地缓存,返回 304。
    • 否则返回新的资源,状态码 200,并更新返回响应的 last-modified 值。
2.按刷新按钮、F5 刷新、网页右键“重新加载

这种情况下,实际是浏览器将 cache-controlmax-age 直接设置成了 0,让缓存立即过期,直接走协商缓存路线。

3.ctrl + F5 强制刷新

强制刷新的情况下,浏览器会强行设置 no-cache,强制获取最新的资源,就连if-modified-since等其他缓存协议字段都会被吃掉。

六、总结

图源
强制缓存和协商缓存