浏览器缓存机制(详)

目录

  • 1,缓存的分类
    • 1.1,按缓存位置
      • 1,Service Worker
      • 2,Memory Cache
      • 3,Disk Cache
      • 4,Push Cache
    • 1.2,按缓存类型
      • 强缓存
        • Expires
        • Cache-control
      • 协商缓存
        • Last-Modified & If-Modified-Since
        • Etag & If-None-Match
  • 2,缓存读取规则
  • 3,浏览器的一些行为
  • 4,缓存最佳实践
    • 频繁变动的资源
    • 不常变化的资源

1,缓存的分类

1.1,按缓存位置

1,Service Worker

MDN参考

它是运行在浏览器后台的独立线程,可以将它理解为是一个代理服务器。可以拦截所有的网络请求,也可以缓存数据。主要是为了创建有效的离线体验,比如后台同步,离线渲染,推送通知等。

它的缓存机制也比较特殊,可以自由的控制:缓存内容,匹配规则,读取规则,有效期等。

实现缓存的大致步骤:

  • 注册 Service Worker。
  • 监听 install 事件提前缓存资源。
  • 监听 fetch 事件,拦截网络请求并返回已缓存的资源。

浏览器缓存机制(详)_第1张图片

2,Memory Cache

它是内存中的缓存,主要缓存当前页面中已经抓取到的资源,例如已经下载的样式、脚本、图片等,保存一份资源的引用,以备下次使用。

注意,Memory Cache 是浏览器为了加快读取缓存速度而进行的自身的优化行为,不受开发者控制,也不受 HTTP Header 的约束,算是一个黑盒。

访问页面后,再次刷新,可以看到很多数据来自内存缓存:
浏览器缓存机制(详)_第2张图片

特点:读取速度快,但时效性很短,会随着进程结束而释放。比如关闭标签页,内存中的缓存就被释放了。

而因为计算机可使用的内存一般都比较小,操作系统对内存的使用会更精细化,所以可供浏览器使用的并不会很多。

作用:该缓存机制保证了一个页面有2个以上相同的资源请求时,实际最多只会被请求一次,如上图所示。

3,Disk Cache

重点

它是存储在硬盘中的缓存,是覆盖面最大的缓存机制,会根据 HTTP Header 中的字段(Cache-Control等)来判断:

  • 哪些资源需要缓存
  • 哪些资源可以不请求直接使用
  • 哪些资源已经过期需要重新请求

并且在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再去请求数据。一般来说,绝大部分缓存都来自 Disk Cache

特点:读取速度较慢,但时效性很好(可设置)。

因为时效性的原因,Disk Cache 也算是持久性存储,所以也会面对容量增长的问题。

而浏览器解决这个问题,是通过特殊的算法把 “最老的” 和 “最有可能过时” 的资源一个一个的删除。

4,Push Cache

称为推送缓存或启发式缓存,是HTTP2 新增的内容。

上面3种缓存都没有命中时,它才会被使用,并且时效性很短,只在 session 会话中存在,会话结束就被释放了。

这个我了解的不多,网上也没有找到合适的文章,就不多做介绍了。

1.2,按缓存类型

其实是对 Disk Cache 进行的分类。

强缓存

当浏览器请求资源后,会优先访问强缓存。存在则返回,否则请求服务器,响应后再次写入强缓存。

作用:直接减少请求数量,是提升最大的缓存策略。通过缓存优化网页性能时是应该首先被考虑的。

实现:通过 HTTP Header 中的字段:ExpiresCache-control

Expires

是 HTTP 1.0 的字段,表示缓存到期的时间,是一个绝对时间(当前时间+缓存时间)

Expires: Wed, 20 Dec 2023 23:09:07 GMT

响应头中设置该字段,告诉浏览器在过期之前不要再次请求。

注意格林威治时间和本地时间的区别:

new Date() // Wed Dec 20 2023 23:09:07 GMT+0800 (中国标准时间)
new Date().toGMTString() // Wed, 20 Dec 2023 15:09:07 GMT

该字段的缺点

由于是绝对时间,用户可以修改本地时间,导致浏览器判断缓存失效而重新请求资源。即便不考虑本地修改的问题,时间误差等因素也会导致浏览器和服务器之间的时间不一致,导致缓存失效。

Cache-control

在已知 Expires 的缺点后,在 HTTP 1.1 中增加该字段,是一个相对时间,表示资源缓存的最大有效时间,该时间内不需要再次向服务器发送请求。

Cache-Control: max-age=3600

下面列举一些 Cache-control 的常用值,更多的字段参考 MDN

  • max-age,最大有效时间

  • must-revalidate(响应头信息),如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是有效性。

  • no-cache,客户端会缓存资源,但每次都得发送请求验证有效性,等价于 max-age=0 must-revalidate

  • no-store,真正意义上的“不要缓存”。所有内容都不走缓存,包括强缓存和协商缓存。

  • private(默认值),所有的内容只有客户端才可以缓存,代理服务器不能缓存。

这些值可以混合使用,优先级如下:(网图,我翻译了一下)
浏览器缓存机制(详)_第3张图片

其他一些注意点:

  • HTTP 1.1 之前,如果想使用 no-cache,通常设置 Pragma: no-cache(这也是 Pragma 字段唯一的取值)。但这个字段只是浏览器约定俗成的实现,缺乏可靠性,只是作为兼容字段出现。
  • HTTP 1.1 开始,Expires 逐渐被 Cache-control 取代。而为了兼容 HTTP 1.0HTTP 1.1 ,实际上这2个字段都会配置。

协商缓存

当强缓存超时失效后,就需要使用协商缓存,有服务器来决定缓存资源是否失效(也就是强缓存中的有效性判断。)

服务器资源为更新时,验证请求返回 304,大致流程如下图示:

浏览器缓存机制(详)_第4张图片

而如果资源更新了,验证请求返回 200

浏览器缓存机制(详)_第5张图片

特点:协商缓存并不会减少请求的数量。但如果是304,请求只会返回一个状态码,没有实际的资源内容,所以会减少响应体体积,来缩短网络传输时间。

协商缓存作为强缓存失效的后备解决方案,一般会和强缓存一起使用。通过2组字段来实现:

  • Last-Modified & If-Modified-Since
  • Etag & If-None-Match
Last-Modified & If-Modified-Since

Last-Modified 是响应头,If-Modified-Since 是请求头。

  1. 首先,服务器通过 Last-Modified 字段告知浏览器,资源最后一次被修改的时间。
Last-Modified: Thu, 21 Dec 2023 00:28:00 GMT
  1. 浏览器将资源和这个字段的值保留在缓存中。
  2. 再次请求相同资源时,浏览器从自己的缓存中找出 “不确定是否过期的” 缓存(也就是强缓存失效,已经命中协商缓存了)。并在请求头的 If-Modified-Since 字段中写入保存的 Last-Modified 的值。
  3. 服务器会将接收到的 If-Modified-Since 的值与服务端的 Last-Modified 字段进行对比。相等则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。

缺点:因为它的时间单位是秒,所以如果资源的更新速度是秒以下的单位,则无法使用该缓存。

Etag & If-None-Match

为了解决上面的问题,所以在出现了这组新的字段。

ETag 是响应头,If-None-Match 是请求头。

ETag 是资源的标识符,一般为一个 hash 值,在服务器存储。

整体流程和 Last-Modified & If-Modified-Since 类似,只是做了替换 Last-Modified --> ETagIf-Modified-Since --> If-None-Match

流程如下图示:

浏览器缓存机制(详)_第6张图片

二者对比:

  • 精度:Etag > Last-Modified,因为 Etag 是一个 hash 值,每次都会改变从而确保精度。
  • 性能:Last-Modified > Etag,因为 Etag 需要服务器通过算法来计算 Hash 值。而 Last-Modified 只需要记录时间。
  • 优先级:服务器校验优先考虑 Etag。

Disk Cache 整体流程图:

浏览器缓存机制(详)_第7张图片

2,缓存读取规则

当浏览器要请求资源时:

  1. Service Worker 中获取内容(如果设置了 Service Worker)。

  2. 查看 Memory Cache

  3. 查看 Disk Cache。细分为:

    • 如果有强制缓存且未失效,则使用强制缓存,不请求服务器。这时的状态码全都是 200。

    • 如果有强制缓存但已失效,使用协商缓存,比较后确定 304 还是 200。

  4. 发送网络请求,等待网络响应。

  5. 把响应内容存入 Disk Cache(如果 HTTP 响应头信息有相应配置的话)。

  6. 把响应内容的引用存入 Memory Cache(无视 HTTP 头信息的配置)。

  7. 把响应内容存入 Service WorkerCache Storage(如果设置了 Service Worker)。

3,浏览器的一些行为

用户对浏览器的不同操作,会触发不同的缓存读取策略。

常见的有3种:

  1. 在地址栏输入地址访问时,浏览器会查找 Disk Cache 中是否有匹配。有则使用,没有发送则发送网络请求。
  2. 普通刷新(F5),因为标签页没有关闭,所以 Memory Cache 是可用的,会优先使用它。其次才是 Disk Cache
  3. 强制刷新(Ctrl / Shift + F5),浏览器不使用缓存,因此在 请求头 Request Headers 中会自动添加 Cache-Control: no-cache(为了兼容还会有Pragma: no-cache),服务器直接返回200和最新内容。

4,缓存最佳实践

频繁变动的资源

Cache-Control: no-cache

或(二者等效)

Cache-Control: max-age=0, must-revalidate

表示浏览器可以缓存资源,但每次使用缓存资源前都必须重新验证其有效性(通过 ETag 或者 Last-Modified)。

这样虽然每次都会发起 HTTP 请求,但当缓存内容仍然有效时可以跳过 HTTP 响应体的下载,来减少响应数据的大小。

不常变化的资源

Cache-Control: max-age=31536000

这类资源,可以设置一个很大的有效时间 31536000(一年)。这样浏览器之后请求相同 url 时会命中强制缓存。

由此带来的更新问题,可以在文件名后添加 hash,版本号等字符,从而达到更改资源 url 的目的。

注意之前的强制缓存并未失效,只是不再使用了而已。


以上。

service-worker-cache 参考

你可能感兴趣的:(浏览器,interview,缓存,前端,浏览器)