总结一下浏览器缓存策略:强缓存和协商缓存。
优先级较高的是强缓存,当强缓存失效的情况下,才会走协商缓存。

强缓存

  • 特点

    • 不会向服务端发送网络请求,直接从缓存中读取资源
    • 请求返回200状态码
    • 在devTool中可以看到size显示from disk cache 或 from memory cache
  • 设置方法
    通过两种HTTP Header 实现:

    • Expires
    • Cache-Control

      Cache-Control优先级高于Expires

      Expires
      通过指定资源到期时间:

    • 这个时间之前无需发起网络请求,直接使用缓存资源;
    • 这个时间之后失效,需要重新发起网络请求获取。

      缺点:受限制于本地时间,可以通过修改本地时间致其失效

      Cache-Control
      可以在请求头或响应头中设置,可以组合使用多种指令:

    • public:相应可以被服务端或客户端缓存
    • private:默认值。响应只可以被客户端缓存
    • max-age=30:缓存30s后过期,需要重新请求
    • s-maxage=30:覆盖max-age,作用一致,代理服务器才生效
    • no-store:不缓存任何响应
    • no-cache:资源能被缓存,但立即失效
    • max-stale=30:30s内,即使缓存过期也使用该缓存
    • min-fresh=30:希望30s内获取最新响应

协商缓存

协商缓存就是强缓存失效后,浏览器携带资源的缓存标识向服务端发起请求,有服务器根据缓存标识决定是否继续使用缓存的过程。
当浏览器发起请求验证资源时,如果资源没有改变,那么服务端就返回304状态码,并且更新浏览器现有缓存的有效期。

协商缓存可以通过设置两种HTTP Header实现:

  • Last-Modified
  • ETag

Last-Modified

浏览器发起请求访问目标资源,服务器在返回资源的同时,会在response header中添加Last-Modified这个header,
标识这个资源在服务器上的最后修改时间。

浏览器下次请求这个资源,浏览器检测到有Last-Modified这个header,于是会添加If-Modified-Since这个header,其值就是Last-Modified中的值。

服务器收到请求后,根据If-Modified-since中的值与这个资源的最后修改时间作对比

  • 若服务器的资源最后修改时间不等于If-Modified-Since中的值,就会将新的资源发送回来
  • 否则,返回304状态码

缺点:

  • 如果修改本地缓存文件,会造成Last-Modified被修改,服务端不能命中缓存导致发送相同资源。
  • Last-Modified只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会正确返回资源。

因为这些缺点,所以在HTTP/1.1 出现了ETag

ETag

ETag是服务器在响应请求时,返回的当前资源的一个唯一标识(服务器生成),只要资源内容有变化,ETag就会重新生成。

浏览器在向服务器发送请求时,会将上一次请求返回的ETag值放到请求头的If-None-Match字段里面

服务器会比较If-None-Match中的值跟目标资源的ETag是否一致:

  • 一致,响应状态码304
  • 不一致,响应状态码200,返回新资源

特点:

  • ETag优先级比Last-Modified高
  • ETag是服务器通过算法计算得出,需要消耗一定时间

两者对比:

  • ETag/If-None-Match优先级高于Last-Modified/If-Modified-Since。
    • 即同时存在,ETag/If-None-Match生效
  • 性能上,Last-Modified要由于ETag。
    • Last-Modified只需要记录时间;
    • ETag需要服务器通过算法计算一个hash值
  • 在精度上,ETag要优于Last-Modified。
    • Last-Modified的时间单位是秒,如果某个文件在1秒内修改了多次,那么它的Last-Modified没有体现出修改;
    • ETag只要文件修改,就会发生改变

执行流程

  1. 强制缓存优先于协商缓存
    • 若强制缓存生效,则直接使用缓存
    • 若不生效,则进行协商缓存
  2. 协商缓存有服务器决定是否使用缓存
    • 若协商缓存失效,则代表该请求的缓存失效,响应200,并返回新的资源和缓存标识,并存入浏览器缓存中
    • 生效则相应304,标识继续使用现有缓存

如果什么缓存策略都没设置,那么浏览器会如何处理

对于这种情况,浏览器一般采用启发式算法:

去响应头中的Date减去Last-Modified值的10%作为缓存时间

缓存策略应用场景

  • 频繁变动的资源

    • 首先使用Cache-Control:no-cache,是浏览器每次都请求服务器
    • 配合ETag或Last-Modified来验证资源是否过期

      这样碎岩不能节省请求数量,但是能显著减少响应数据的大小

  • 代码文件

    缓存除HTML外文件

    一般来说,都会使用打包工具来打包代码,就可以对HTML文件引用的静态资源的文件名进行哈希处理,只有当文件内容发生修改后才会生成新的文件名。

    因此,可以给代码文件设置缓存有效期1年Cache-Control:max-age=31536000,这样只有当HTML文件中引入的静态文件名变化了才回去下载最新的代码文件,否则一直使用缓存。