浏览器缓存之强缓存与协商缓存

5,109 阅读7分钟

背景

浏览器缓存机制在性能优化中扮演重要的一环。优秀的缓存策略能够缩短请求网页的距离、降低延迟和网络负荷、减少请求带宽。那该如何应用好浏览器缓存,我们需要对其原理有一定认识。

浏览器缓存机制

浏览器中缓存可分为强缓存和协商缓存。具体判断使用那种缓存机制,是通过 http header字段的不同。

浏览器缓存机制过程

  • 浏览器加载资源,根据资源的 http header 判断是否命中强缓存
    • 若命中:浏览器直接从自己缓存中读取资源,不会发生 http 请求到服务器;
    • 没有命中:浏览器会发送请求到服务器,通过服务器中 http header 验证这个资源是否命中协商缓存
      • 命中:请求返回,但不返回资源,告诉客户端可直接从缓存中加载;
      • 没有命中:请求返回,返回资源;

可以得出:强缓存与协商缓存区别:强缓存不发生请求到服务器,协商缓存会发请求到服务器。
下面,我们需要知道 http header 如何判断命中强缓存和协商缓存的。

强缓存

Expires

Expires: Thu, 21 Nov 2019 07:48:15 GMT


ExpiresHTTP/1.0 控制网页缓存的字段。其值为服务器返回该请求结果缓存的到期时间,即如果发生时间在 Expires 之前,那么本地缓存始终有效,否则就会发送请求到服务器来获取资源;是绝对时间;

Cache-Control

Cache-ControlHTTP/1.1 新增的规则,用于控制网页缓存的字段。

  • 示例分析

image.png
 
image.png

  • cache-control: max-age=2592000,s-maxage=3600
    • max-age: 资源第一次的请求时间和 Cache-Control max-age 设定的有效期,计算出资源过期时间;再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则不行。是相对时间;
  • cache-control: public, max-age=31536000
    • public 可以被所有用户浏览器缓存,包括代理服务器,时长为第一次请求资源时间与31536000秒之和。

Expires & Cache-Control

我们需要知道 Cache-Control 与 Expires 同时存在的话(如下图),Cache-Control** 的优先级高于 **Expires

image.png

因为 Expires 时间返回的是服务器绝对时间,而客户端本地时间是可以修改的(时区不同等),造成服务器与客户端时间发生误差,强缓存会直接失效。而 Cache-Control 是相对时间,每次参照客户端第一次请求时间计算而来的,故不会受到影响;毕竟 Cache-ControlHTTP/1.1 新增的规范

查看浏览器已经成功应用使用了强缓存

一旦资源命中强缓存, 浏览器便不会向服务器发送请求, 而是直接读取缓存。 Chrome 下的现象如下 200 OK disk cache 或者 200 OK from memory cache

image.png
         
image.png

image.png

disk cache & memory cache

看到上图,应该会有疑问,from memory cachefrom disk cache 又分别代表的是什么呢?
根据英文名 memory disk ,我们需要知道内存缓存和硬盘缓存两个概念;

内存缓存

内存缓存即 memory cache

内存缓存有两个特点 快速读取 和 时效性     快速读取:会将解析编译资源放入到进程中的内存,占据一定内存资源,方便下次快速读取     时效性:一旦进程关闭,进程的内存会被清空

硬盘缓存

硬盘缓存即 disk cache

硬盘缓存将数据写入到磁盘文件,读取缓存需要读取硬盘文件进行 I/O 操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

读取缓存时机

什么时候从内存缓存读取,什么时候从硬盘缓存读取?
在浏览器中,浏览器会在 JS 和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时,直接从内存缓 存 from memory cache 中读取,而 CSS 比较大的文件则会存入硬盘文件,所以每次渲染页面都需要从磁盘读取缓存 from disk cache

  • 根据例子解释说明

打开一个网页此时是开启一个新进程,内存中还没缓存文件。所以从硬盘缓存 from disk cache 中读取,如下图

image.png

当刷新网页,内存中已经有缓存缓存文件,故有的会从内存缓存 from memory cache 中读取

image.png

协商缓存

Last-Modified

Last-Modified: Wed, 21 Nov 2018 05:46:58 GMT
If-Modified-Since: Wed, 21 Nov 2018 05:46:58 GMT

具体过程如下:

  • 1、浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,会在 ResponeHeaderLast-Modified 值设置为,该资源最后修改的时间;
  • 2、第二次请求的时候,在 RequestHeader 上加上 If-Modified-Since,值为上次请求资源的 Last-Modified
  • 3、服务器收到 If-Modified-Since 与服务器文件的 Last-Modified 比对,
    • 命中:无变化则返回 304,不返回资源。浏览器收到 304 使用本地缓存;不更新 Last-Modified
    • 不命中:有变化返回 200,重新更新 Last-Modified、返回 200、返回资源。

ETag

Etag 是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)。

ETag: "d5d-55b192d5e0640"
If-None-Match: "d5d-55b192d5e0640"

具体过程如下:

  • 1、浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,会在 ResponeHeaderETag 值设置为,该资源当前资源文件的一个唯一标识;
  • 2、第二次请求的时候,在 RequestHeader 上加上 If-None-Match,值为上次请求资源的 ETag
  • 3、服务器收到 If-None-Match 与服务器文件的 ETag 比对,
    • 命中:一致则返回 304,代表资源无更新,故不返回资源。浏览器将会收到 304 使用本地缓存;更新 ETag
    • 不命中:不一致返回 200,重新更新 ETag、返回 200、返回资源。

我们可以得知具体过程与 Last-Modified 过程一致,只有有一点区别如下:

  • 当服务器返回 304 Not Modified 的响应时,由于 ETag 重新生成过,ResponeHeader 还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。

Last-Modified & Etag

Last-ModifiedETag 是可以一起使用的(见下图),服务器会优先验证 ETag ,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304 Not Modified

ETag 可以解决 Last-Modified 存在的一些问题,既生 Last-Modified 何生 ETag ?

  • 文件内容不更改,但修改时间发生改变,这时候不希望客户端认为这个文件修改了。
  • 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说 1S 内修改了 N 次),If-Modified-Since 能检查到的粒度是 S 级的,这种修改无法判断;
  • 某些服务器不能精确的得到文件的最后修改时间。

image.png

image.png

总结

强缓存会优先于协商缓存,若强缓存 ( Expires / Cache-Control ) 生效,直接不发送请求直接使用本地资源;不生效接着进行协商缓存。协商缓存 ( Last-Modified / ETag ) 由服务器进行判断,若文件失效,返回新的资源,否则返回 304,不返回资源。