浏览器基础知识-浏览器缓存

0 阅读13分钟

对浏览器的缓存机制的理解(重点)

浏览器缓存的全过程

  1. 浏览器第一次加载资源,服务器返回 200,浏览器从服务器下载资源文件,并缓存资源文件与响应头,以供下次加载时对比使用。

  2. 下一次加载资源时,由于强制缓存优先级较高,先比较当前时间与上一次返回 200 时的时间差,如果没有超过 cache-control 设置的 max-age,则没有过期,并命中强缓存,直接从本地读取资源。

    如果浏览器不支持 HTTP1.1,则使用 expires 判断是否超过资源的过期时间。

  3. 如果资源已过期,则表明强制缓存没有被命中,则开始协商缓存,向服务器发送带有 If-None-Match 和 If-Modified-Since 的请求。

  4. 服务器收到请求后,优先根据 Etag 的值判断被请求的文件有没有做修改。

    Etag 值一致则没有修改,命中协商缓存,返回 304;如果不一致则有改动,直接返回新的资源文件带上新的 Etag 值并返回 200。

  5. 如果服务器收到的请求没有 Etag 值,则将 If-Modified-Since 和被请求文件的最后修改时间做比对。

    值一致则命中协商缓存,返回 304;不一致则返回新的 Last-Modified 和文件并返回 200。

强缓存(本地验证)

Cache-Control: max-age=3600
Expires: Mon, 15 Jan 2024 12:00:00 GMT # 浏览器不支持 HTTP1.1 时使用

协商缓存(服务器验证)

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Mon, 15 Jan 2024 00:00:00 GMT # 浏览器不支持 HTTP1.1 时使用

版本号的作用

很多网站的资源后面都加了版本号,这样做的目的是:每次升级了 JS 或 CSS 文件后,为了防止浏览器进行缓存,强制改变版本号,客户端浏览器就会重新下载新的 JS 或 CSS 文件 ,以保证用户能够及时获得网站的最新更新。

浏览器资源缓存的位置有哪些?(重点)

缓存位置优先级

资源缓存的位置一共有 3 种,按优先级从高到低分别是:

  1. Service Worker

    Service Worker 运行在 JavaScript 主线程之外,虽然由于脱离了浏览器窗体无法直接访问 DOM,但是它可以完成离线缓存、消息推送、网络代理等功能。

    它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。

    当 Service Worker 没有命中缓存的时候,需要去调用 fetch 函数获取数据。也就是说,如果没有在 Service Worker 命中缓存,会根据缓存查找优先级去查找数据。但是不管是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示是从 Service Worker 中获取的内容。

    概括:可编程缓存,离线应用。

  2. Memory Cache

    Memory Cache 就是内存缓存,它的效率最快。

    内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。

    概括:内存缓存,速度快但容量小。

  3. Disk Cache

    Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比 Memory Cache 胜在容量和存储时效性上。

    在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Header 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。

    概括:磁盘缓存,容量大但速度慢。

  4. Push Cache

    Push Cache 是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。并且缓存时间也很短暂,只在**会话(Session)**中存在,一旦会话结束就被释放。

    特点:

    1. 所有的资源都能被推送,但是 Edge 和 Safari 浏览器兼容性不好
    2. 可以推送 no-cache 和 no-store 的资源
    3. 一旦连接被关闭,Push Cache 就被释放
    4. 多个页面可以使用相同的 HTTP/2 连接,也就是说能使用同样的缓存
    5. Push Cache 中的缓存只能被使用一次
    6. 浏览器可以拒绝接受已经存在的资源推送
    7. 可以给其他域名推送资源

    概括:HTTP/2 推送缓存。

缓存查找顺序

Service Worker Cache ->
Memory Cache ->
Disk Cache ->
Push Cache ->
Network Request(网络请求)

强缓存和协商缓存的区别(重点)

强缓存

使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。

强缓存策略可以通过两种方式来设置,分别是 http 头信息中的 Expires 属性和 Cache-Control 属性。

Expires

服务器通过在响应头中添加 Expires 属性,来指定资源的过期时间。在过期时间以内,该资源可以被缓存使用,不必再向服务器发送请求。这个时间是一个绝对时间,它是服务器的时间。

可能存在的问题,就是客户端的时间和服务器端的时间不一致,或者用户可以对客户端时间进行修改的情况,这样就可能会影响缓存命中的结果。

Cache-Control

Expires 是 http1.0 中的方式,因为它的一些缺点,在 HTTP 1.1 中提出了一个新的头部属性就是 Cache-Control 属性,它提供了对资源的缓存的更精确的控制。它有很多不同的值,可设置的字段包括:

  1. public:设置了该字段值的资源表示可以被任何对象(包括:发送请求的客户端、代理服务器等等)缓存。这个字段值不常用,一般还是使用 max-age= 来精确控制。

  2. private:设置了该字段值的资源只能被用户浏览器缓存,不允许任何代理服务器缓存。在实际开发当中,对于一些含有用户信息的 HTML,通常都要设置这个字段值,避免代理服务器(CDN)缓存。

  3. no-cache:设置了该字段表示在使用已存储的缓存副本之前,必须先向服务器发起验证,确认该资源是否仍然有效(未过期或未被修改)。如果服务器返回 304 Not Modified,则可以使用缓存;否则,使用服务器返回的新资源。

  4. no-store:设置了该字段表示禁止任何缓存,每次都会向服务端发起新的请求,拉取最新的资源。

  5. max-age=:设置缓存的最大有效期,单位为秒。

  6. s-maxage=:优先级高于max-age=,仅适用于共享缓存(CDN),优先级高于 max-age 或者 Expires 头。

  7. max-stale[=seconds]:设置了该字段表明客户端愿意接收已经过期的资源,但是不能超过给定的时间限制。

no-cache 和 no-store 的区别

  1. no-cache 是指先要和服务器确认是否有资源更新,再进行判断。也就是说没有强缓存,但是会有协商缓存。
  2. no-store 是指不使用任何缓存,每次请求都直接从服务器获取资源。

优先级

一般来说只需要设置其中一种方式就可以实现强缓存策略,当两种方式一起使用时,Cache-Control 的优先级要高于 Expires


协商缓存

如果命中强制缓存,我们无需发起新的请求,直接使用缓存内容;如果没有命中强制缓存,如果设置了协商缓存,这个时候协商缓存就会发挥作用了。

命中条件

命中协商缓存的条件:ETag 或 Last-Modified 存在,并且请求头中的 If-None-Match 或 If-Modified-Since 与服务器端的值匹配,表示资源没有变化,返回 304 响应,命中缓存。

ETag 配合 If-None-Match 使用,属于 http1.1 协议;Last-Modified 配合 If-Modified-Since 使用,属于 http1.0 协议。

工作原理

使用协商缓存策略时,会先向服务器发送一个请求。如果资源没有发生修改,则返回一个 304 状态,让浏览器使用本地的缓存副本;如果资源发生了修改,则返回修改后的资源。

Last-Modified

服务器通过在响应头中添加 Last-Modified 属性来指出资源最后一次修改的时间,当浏览器下一次发起请求时,会在请求头中添加一个 If-Modified-Since 的属性,属性值为上一次资源返回时的 Last-Modified 的值。当请求发送到服务器后,服务器会通过这个属性来和资源的最后一次的修改时间来进行比较,以此来判断资源是否做了修改。如果资源没有修改,那么返回 304 状态,让客户端使用本地的缓存。如果资源已经被修改了,则返回修改后的资源。

使用这种方法有一个缺点,就是 Last-Modified 标注的最后修改时间只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,那么文件已将改变了但是 Last-Modified 却没有改变,这样会造成缓存命中的不准确。

Etag

因为 Last-Modified 可能发生的不准确性,http 中提供了另外一种方式,那就是 Etag 属性。

服务器在返回资源的时候,在头信息中添加了 Etag 属性,这个属性是资源生成的唯一标识符,当资源发生改变的时候,这个值也会发生改变。在下一次资源请求时,浏览器会在请求头中添加一个 If-None-Match 属性,这个属性的值就是上次返回的资源的 Etag 的值。服务接收到请求后会根据这个值来和资源当前的 Etag 的值来进行比较,以此来判断资源是否发生改变,是否需要返回资源。

优先级

当响应中同时包含 Last-Modified 和 ETag 时,浏览器在发起验证请求时会同时携带 If-Modified-Since 和 If-None-Match 头。服务器优先以 If-None-Match(即 ETag)为准。若 ETag 匹配,直接返回 304 Not Modified;若 ETag 不匹配,则说明资源已变化,服务器通常直接返回 200 和新资源内容,不再回退检查 If-Modified-Since。

少数服务器实现可能会在 ETag 缺失或格式无效时降级使用 Last-Modified,但在两者均存在且 ETag 有效的情况下,ETag 拥有决定权。这种设计是因为 ETag 比基于时间的 Last-Modified 更精确,能应对文件内容未变但修改时间变化等场景。

负载平衡考虑

在负载均衡环境中,为避免因 ETag 不一致导致缓存失效,不应简单禁用 ETag,而应确保所有后端服务器对相同资源生成一致的 ETag 值。若无法实现强 ETag 一致(如受限于服务器索引节点等差异),可使用弱 ETag 或统一 ETag 生成策略。CDN 和负载均衡器无法替代后端服务器生成 ETag,需在应用或 Web 服务器层面解决。

总结

  • 强缓存策略和协商缓存策略在缓存命中时都会直接使用本地的缓存副本,区别只在于协商缓存会向服务器发送一次请求。
  • 它们缓存不命中时,都会向服务器发送请求来获取资源。
  • 在实际的缓存机制中,强缓存策略和协商缓存策略是一起合作使用的。浏览器首先会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则服务器返回最新的资源给浏览器。

为什么需要浏览器缓存?

核心目标

对于浏览器的缓存,主要针对的是前端的静态资源

最好的效果就是,在发起请求之后,拉取相应的静态资源,并保存在本地。如果服务器的静态资源没有更新,那么在下次请求的时候,就直接从本地读取即可,如果服务器的静态资源已经更新,那么我们再次请求的时候,就到服务器拉取新的资源,并保存在本地。这样就大大的减少了请求的次数,提高了网站的性能。这就要用到浏览器的缓存策略了。

定义

浏览器缓存,指的是浏览器将用户请求过的静态资源,存储到电脑本地磁盘中,当浏览器再次访问时,就可以直接从本地加载,不需要再去服务端请求了。

优点

  1. 减少了服务器的负担,提高了网站的性能。
  2. 加快了客户端网页的加载速度
  3. 减少了多余网络数据传输

地址栏回车、点击刷新按钮或者按 F5、按 Ctrl+F5 (强制刷新),有什么区别?

  1. 地址栏回车: 浏览器发起请求,按照正常流程,本地检查是否过期,然后服务器检查新鲜度,最后返回内容。

  2. 点击刷新按钮或者按 F5:浏览器直接对本地的缓存文件过期,但是会带上 If-Modifed-Since、If-None-Match,这就意味着服务器会对文件检查新鲜度,返回结果可能是 304,也有可能是 200。

  3. 用户按 Ctrl+F5(强制刷新):浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since、If-None-Match,相当于之前从来没有请求过,返回结果是 200。

总结

浏览器缓存是前端性能优化中非常重要的一环,合理利用缓存策略可以显著提升网站的加载速度和用户体验。通过本文的介绍,我们了解了浏览器缓存的基本原理、缓存位置、强缓存和协商缓存的区别,以及不同刷新方式对缓存的影响。希望这些知识能够帮助你在实际开发中更好地应用浏览器缓存,提升网站性能。