浏览器相关-缓存

2019-06-30 15:29:592021-05-16 15:24:15

对于一个数据请求来说,可以分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。比如说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据

缓存的意义就在于减少请求,更多地使用本地的资源,给用户更好的体验的同时,也减轻服务器压力。所以,最佳实践,就应该是尽可能命中强缓存,同时,能在更新版本的时候让客户端的缓存失效。

缓存位置

  • Service Worker

    Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。

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

  • Memory Cache

    Memory Cache 也就是内存中的缓存,读取内存中的数据肯定比磁盘快。但是内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。

    当访问过页面以后,再次刷新页面,可 以发现很多数据都来自于内存缓存

  • Disk Cache

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

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

  • Push Cache

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

缓存策略

通常浏览器缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的。

  1. 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果及缓存标识
  2. 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中
  • 浏览器在加载资源时,先根据这个资源的一些http header判断它是否命中强缓存,强缓存如果命中,浏览器会直接从自己的缓存中读取资源,不会发送请求到浏览器
  • 当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器依据资源的另外一些http header验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回,但是不会返回这个资源的数据,而是告诉客户端直接从缓存中加载这个资源,于是浏览器就又会从自己的缓存中去加载这个资源
  • 强缓存与协商缓存的共同点时:如果命中都是从客户端缓存中加载资源,而不是从服务器加载资源数据,区别是:强缓存不发送请求到服务器,协商缓存会发送请求到服务器
  • 当协商缓存也没有命中的时候,浏览器直接从服务器加载资源数据

cache流程

强缓存

强缓存可以通过设置两种 HTTP Header 实现:Expires Cache-Control 。强缓存表示在缓存期间不需要请求,state code 为 200

  • Expires

    Expires: Wed, 22 Oct 2018 08:41:00 GMT

    Expires 是 HTTP/1 的产物,表示资源会在 Wed, 22 Oct 2018 08:41:00 GMT 后过期,需要再次请求。并且 Expires 受限于本地时间,如果修改了本地时间,可能会造成缓存失效。

  • Cache-control

    Cache-control: max-age=30

    Cache-Control 出现于 HTTP/1.1,优先级高于 Expires 。该属性值表示资源会在 30 秒后过期,需要再次请求。

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

    cache header字段

协商缓存

强缓存判断是否缓存的依据来自于是否超出某个时间或者某个时间段而不关心服务端文件是否已经更新,这可能会导致加载文件不是服务器端最新的内容,那么此时就需要协商缓存来确定服务端内容是否发生更新。

协商缓存就是在强缓存失效后,浏览器携带标识向服务器发起请求,由服务器根据缓存不碍事决定是否使用缓存的过过程。协商缓存可通过设置两种 HTTP Header 实现:Last-Modified 和 ETag 。

  • Last-Modified 和 If-Modified-Since

    Last-Modified 会随着第一次访问资源时返回(该资源在浏览器上最后的修改日期)。浏览器再次请求该资源时,If-Modified-Since 会将 Last-Modified 的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来,否则返回 304 状态码-即命中缓存

    弊端:

    1. 如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成 Last-Modified 被修改,服务端不能命中缓存导致发送相同的资源
    2. 因为 Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源
  • ETag 和 If-None-Match

    ETag 类似于文件指纹,,会随着第一次访问资源时返回(浏览器生成)。浏览器再次请求该资源时,If-None-Match 会将当前 ETag 发送给服务器,询问该资源 ETag 是否变动,有变动的话就将新的资源发送回来。并且 ETag 优先级比 Last-Modified 高

实际使用场景

  • 频繁变动的资源

    对于频繁变动的资源,首先需要使用 Cache-Control: no-cache 使浏览器每次都请求服务器,然后配合 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。

  • 代码文件

    一般来说,现在都会使用工具来打包代码,那么我们就可以对文件名进行哈希处理,只有当代码修改后才会生成新的文件名。基于此,我们就可以给代码文件设置缓存有效期一年 Cache-Control: max-age=31536000,这样只有当 HTML 文件中引入的文件名发生了改变才会去下载最新的代码文件,否则就一直使用缓存。