浏览器缓存策略是怎样的

229 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情

核心描述

  • 浏览器中缓存的优先级:
    • 强缓存 > 协商缓存,即浏览器优先判断请求头中关于强缓存相关的字段是否处于生效阶段,如果生效则直接使用缓存,如果不生效则开始判断请求头中协商缓存相关字段,如果也不生效,则会直接向服务器请求资源
    • 强缓存:Pragma > Cache-Control > Expires
    • 协商缓存:Etag > Last-Modified
  • 强缓存:强缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存的过程。
    • Pragma 请求头:
      • HTTP 1.0 协议相关,已不常用
      • 只有一个属性值,no-cache,效果与 Cache-Control 中的 no-cache 效果一致,不使用强缓存,需要与服务器验证缓存是否新鲜,在3个强缓存的请求头中优先级最高
    • Cache-Control 请求头:
      • HTTP 1.1 中新增的属性,现在为浏览器默认优先使用的属性
      • 设置的值为相对值,常用的 max-age ,单位是秒
    • Expires 请求头:
      • HTTP 1.0 协议相关,已不常用
      • 设置的值为绝对值,是一个 HTTP 日期
      • 在浏览器发起请求时,会根据系统时间和 Expires 的值进行比较,如果系统时间超过了 Expires 的值,缓存失效
      • 由于和系统时间比较,所以当系统时间和服务器时间不一致时,会有缓存有效期不准的问题
      • 在3个强缓存请求头中优先级最低
  • 协商缓存:协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
    • Etag / If-None-Match:(是标识)
      • Etag 是服务端响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)
      • If-None-Match 是当客户端再次发起请求时,携带上次请求返回的唯医标识 Etag 的值,用于服务端做匹配,如果一致则表示文件没有改动,返回 304,让浏览器继续使用缓存文件,如果不一致,则返回 200,并返回新的资源文件
      • Etag / If-None-Match 在协商缓存中的优先级最高
    • Last-Modified / If-Modified-Since:(是时间)
      • Last-Modified 是服务器响应请求时,返回该资源文件的服务器最后修改的时间
      • If-Modified-Since 是客户端再次发起请求时,携带的上次请求返回的 Last-Modified 值,通过此字段告诉服务器该资源上次请求返回的最后被修改时间,如果该时间小于服务器上该资源最后修改时间,则返回 200,并重新返回新的资源,如果等于服务器上该资源最后修改时间,则返回 304,让浏览器继续使用缓存文件
  • 个人理解:简单总结下来,可以认为浏览器的缓存,主要分为强缓存和协商缓存,如果在缓存时间内,则使用缓存文件,如果超出了缓存有效期,则需要触发协商缓存,如果协商缓存也没有触发缓存,则会从浏览器中直接获取
    • 强缓存主要看 Cache-Control 的值,如果在缓存时间内,则使用缓存文件,如果超出了缓存有效期,则需要触发协商缓存
      • 不同情况从不同地方取,如果对应的也 tab 还处于活跃状态,则从内存中取
      • 如果对应的页面 tab 已经关闭,重新打开,或是由于浏览器内存原因被释放,则会从硬盘中读取
      • 虽然从硬盘中取的速度要比从内存中取的速度慢,但仍然比触发一次网络请求要快
      • 除了 Cache-Control 还有两个已经不常用的,Pragma 和 Expires,3者的优先级是 Pragma 最高,Expires 最低
    • 协商缓存主要有两个,一个是文件修改时间,一个是文件修改标识
      • Etag / If-None-Match,是修改文件标识,服务端会根据文件内容,计算出对应的标识符,会消耗一定的计算时间
      • Last-Modified / If-Modified-Since,是文件最后的修改时间
      • 两者的优先级:Etag / If-None-Match 大于 Last-Modified / If-Modified-Since
    • 强缓存和协商缓存并不是毫无关系,或者二选一的状态,而是浏览器针对请求的文件在缓存策略上的一个优先级的关系,所以从这个角度理解之后,会发现缓存相关的很多请求头的属性值其实都是有关联的

知识拓展

  • Cache-Control 常见的取值:
    • max-age:单位是秒,缓存时间计算方式是距离发起的时间的秒数,超过间隔的描述缓存失效
    • no-cache:不使用强缓存,需要服务器验证缓存是否为最新(并不是说禁用缓存,只是需要请求一次服务器)
    • no-store:禁用缓存(包括协商缓存),每次都向服务器请求最新的资源
    • private:专用于客户端的缓存,即中间代理、CDN等不能缓存此响应
    • public:客户端、中间代理、CDN等服务器都可以缓存
    • must-revalidate:在缓存过期前可以使用,过期后必须向服务器验证(需要注意的是,此属性并非 http 协议中要求,更像是 chrome 自己实现的一个属性,所以是否完全起作用,不确定)
  • 命中强缓存时,常看到的 from cache 说明:
    • from memory cache:内存缓存
      • 快速读取和时效性的特点,快速读取即资源在浏览器内存中,相比于硬盘中的读取速度更快,时效性即当页面进程关闭时,对应的内存也被清空
      • 在浏览器中,浏览器会在 JS 和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时,直接从内存缓 存 from memory cache 中读取,而 CSS 比较大的文件则会存入硬盘文件,所以每次渲染页面都需要从磁盘读取缓存 from disk cache。
    • from disk cache:硬盘缓存
      • 容量更大一些的资源会存在硬盘中,对应的读取时间较内存读取会长一些
      • 当页面进程关闭后,对应的硬盘缓存不会清空,只要缓存时间没有过期,均可以再次从硬盘中读取
    • 访问速度:from memory cache > from disk cache > 发起一次网络请求
    • 在隐私模式下,均为 from memory cache
    • 这也是为什么 chrome 浏览器更占内存的原因,因为很多资源都被缓存到内存中
  • 其他拓展:
    • PWA 方向:Service Worker \ manifest.json
    • localStorage:将静态资源缓存至 localStorage 中,在 App 中可以提高一定的体验,对缓存更新的策略要求相对较高
    • 利用 webpack 的 hash 文件命名来取代 Etag 方案,如果是 SPA 应用,可以将 index.html 模板页面设置为 no-cache 或 no-store 保证其最新,这样就能直接获取页面中最新的 hash 资源文件
    • applicationCache (已被废弃)

参考资料

浏览知识共享许可协议

本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。