关于缓存的简单了解

111 阅读6分钟

面试的时候经常会被问 304状态码 是什么情况下产生的,如何判断缓存是否可用巴拉巴拉类似的问题。关于浏览器缓存,作用想必大家都清楚,减少没必要的请求,最重要的出发点还是提升用户体验,其次减轻服务器压力。常见的有 DNS缓存、CDN缓存、HTTP缓存等,今天我们主要简单介绍下 http缓存叭~

HTTP 缓存

适用阅读场景:前端了解 http缓存,可解释开发中常见的一些简单的现象或问题,或“应付”该模块面试(讲清楚缓存理念即可,不需要过分详细,毕竟只是现阶段的缓存规则而已)。

清下缓存?试试强制刷新页面?细心的你是否发现前进后退页面渲染很快?不着急,我们先来聊聊 http缓存 的两个重要阶段:强缓存协商缓存

我们先来看一下一般行为,http缓存校验顺序为:先在强缓存阶段校验,不通过则校验协商缓存。

强缓存

cache-control: max-age

http请求 先经过资源的有效期是接口决定的,因此决定缓存的字段定义在响应头中,看图 👇

image.png

如图可见响应头字段 cache-control 中包含指令 max-age,其值的单位为 's',表示该资源有效期为 120s,超时强缓存校验失效。

  • 首次请求资源,本地无缓存,请求到达服务器,待返回结果后缓存请求。这一步将在响应头出现上图所示 cache-control: max-age=xxx;
  • 再次请求该接口,浏览器取到本地缓存,根据 max-age=xxx 判断资源是否过期,未过期,则缓存命中,返回缓存资源及状态码 200,浏览器未向服务器发起请求,整个请求结束; 如资源过期,则进入下一步 协商缓存 校验。
expires(补充,不常用)

http 1.0 规范的强缓存字段为 expires,key-value 形式为:

expires: Wed, 21 Oct 2021 07:28:00 GMT

该字段目前不是很常见了,优先级要低于 cache-control: max-age,被替代的原因是 expires 返回的是服务器时间,而判断强缓存是否有效发生在客户端,当两者时间不一致时,可能会返回过期资源或未过期资源的重复请求。

协商缓存

该校验发生在服务器侧,如图框选 👇

image.png

last-modified / if-modified-since
  • 客户端首次发起请求,服务器返回last-modified,资源缓存在本地;
  • 再次请求,请求头带 if-modified-since,服务器将该值与最新文件更新时间比较,校验是否过期,未过期则缓存命中,返回 304;否则判断是否有 Etag 字段,无则返回最新文件及状态码 200,重新缓存请求至本地。若包含 Etag则进行下一步判断。

缺点:

  1. 该时间最小粒度为s,发生在 1s 内的变更会被忽略
  2. 一些频繁更新的文件,更新时间变化,但内容无变化,导致不必要的再次传输
Etag / if-none-match

优先级高于 last-modified / if-modified-since

该校验过程与 last-modified / if-modified-since 校验流程一致,原理上,Etag 为根据文件内容计算的 hash 值,可以判断文件是否发生过变化,未变化则返回 304,告诉浏览器返回缓存,否则返回最新文件及状态码 200

🚩 实际开发中,我们一般可能采用的缓存策略是

  • 协商缓存:html,这块资源一般比较小,很少发生变化

  • 强缓存:image、js、css 等,一般开发中会在这些资源上加 hash,缓存时间内都不需要向服务器发起请求,更新资源后 url 变化缓存自动失效,不会影响资源更新。

Service workers 缓存

在讲 Service workers之前还是要说一下 Web Worker, 大家都知道 js 同步任务是在单线程中运行的,在一些长任务场景比如海量地图经纬度转换时,为避免长时间占用主线程,设计了 Web Worker,它运行在主线程之外,通过 postMessage 与主线程互相通信,有效减少了主线程压力(慎重使用,新建 worker 实例、线程之间通信也都是消耗)。Service workers 保持了 Web Worker 的运行在主线程之外的特性,不同的是,后者执行完线程就会退出,前者运行在浏览器进程中,可为多个页面提供服务。再一个就是 Service workers 增加了缓存功能。

Service workers 的主要作用就是离线缓存,目的是在无网络状态也可以通过缓存资源正常渲染页面,简单来讲就是以下两点👇:

  • 缓存资源Service workers 中可以添加缓存规则,在有网状态根据规则缓存资源;
  • 请求拦截:在页面网络与服务器之间架起一个拦截器,在页面请求发起时,检测如果是无网状态,则跳过向服务器发起请求,直接返回缓存资源。

🚩 tips: 建议大家有时间可以看下红宝书 Service workersfetch 章节🌝 然后结合一些 PWA 的文章,可以进一步理解这部分内容~

可参考 獨釣寒江雪关于的 PWA 文章~

补充

  • network 中可以看到请求字段 size常见三种状态:memory cache | disk cache | xxkb,简单介绍下常见缓存位置

    • memory cache 存储在内存中的缓存,当关闭浏览器,资源从内存释放
    • disk cache 存储在磁盘中的缓存,下次依然来自于磁盘
    • Service Worker 上边已经简单介绍过了,详情可以参考 MDN
    • Push Cache 会话缓存,其次有效期很短,其他自行查阅🤥 不是很了解

    🚩 补充:资源存储在 memory cache 还是 disk cache 的规则是? 结合 memory cache 存储空间小有效性随会话关闭而消失,disk cache 空间大,可永久保存的特点,一般大文件优先存储在 disk cache,或者在内存吃紧的情况,也会存储到 disk cache。而 memory cache 一般缓存小文件,比如经常发现一些页面中 base64 资源来自 memory cache,如 www.baidu.com 首页👇

image.png

🚩 补充: xxkb 具体的 size 值,在 `200` 状态码下,表示资源大小,而在 `304` 状态码下,则表示协商缓存服务端通信报文的大小。
  • 下面几种情况都走了那些缓存校验阶段:
    • 刷新页面:相当于 no-cache,跳过强缓存,直接检查协商缓存
    • 强制刷新:相当于 no-store,跳过强缓存和协商缓存,直接请求资源
    • 清除缓存并强制刷新:缓存清空,相当于第一次请求资源
    • 前进、后退:只会取缓存,即使缓存失效也会取缓存,无论 cache-control 如何设置。