面试的时候经常会被问 304状态码 是什么情况下产生的,如何判断缓存是否可用巴拉巴拉类似的问题。关于浏览器缓存,作用想必大家都清楚,减少没必要的请求,最重要的出发点还是提升用户体验,其次减轻服务器压力。常见的有 DNS缓存、CDN缓存、HTTP缓存等,今天我们主要简单介绍下 http缓存叭~
HTTP 缓存
适用阅读场景:前端了解 http缓存,可解释开发中常见的一些简单的现象或问题,或“应付”该模块面试(讲清楚缓存理念即可,不需要过分详细,毕竟只是现阶段的缓存规则而已)。
清下缓存?试试强制刷新页面?细心的你是否发现前进后退页面渲染很快?不着急,我们先来聊聊 http缓存 的两个重要阶段:
强缓存、协商缓存
我们先来看一下一般行为,http缓存校验顺序为:先在强缓存阶段校验,不通过则校验协商缓存。
强缓存
cache-control: max-age
http请求 先经过资源的有效期是接口决定的,因此决定缓存的字段定义在响应头中,看图 👇
如图可见响应头字段 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 返回的是服务器时间,而判断强缓存是否有效发生在客户端,当两者时间不一致时,可能会返回过期资源或未过期资源的重复请求。
协商缓存
该校验发生在服务器侧,如图框选 👇
last-modified / if-modified-since
- 客户端首次发起请求,服务器返回
last-modified,资源缓存在本地; - 再次请求,请求头带
if-modified-since,服务器将该值与最新文件更新时间比较,校验是否过期,未过期则缓存命中,返回304;否则判断是否有Etag字段,无则返回最新文件及状态码200,重新缓存请求至本地。若包含Etag则进行下一步判断。
缺点:
- 该时间最小粒度为
s,发生在1s内的变更会被忽略 - 一些频繁更新的文件,更新时间变化,但内容无变化,导致不必要的再次传输
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 workers 与 fetch 章节🌝 然后结合一些 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首页👇
🚩 补充: xxkb 具体的 size 值,在 `200` 状态码下,表示资源大小,而在 `304` 状态码下,则表示协商缓存服务端通信报文的大小。
- 下面几种情况都走了那些缓存校验阶段:
- 刷新页面:相当于
no-cache,跳过强缓存,直接检查协商缓存 - 强制刷新:相当于
no-store,跳过强缓存和协商缓存,直接请求资源 - 清除缓存并强制刷新:缓存清空,相当于第一次请求资源
- 前进、后退:只会取缓存,即使缓存失效也会取缓存,无论
cache-control如何设置。
- 刷新页面:相当于