目录
前言
一、按缓存位置分类
HTTP状态码及区别
几种状态的执行顺序
Memory Cache
Disk Cache
Service Worker
请求网络
二、HTTP 缓存
HTTP 缓存分类
强缓存原理
协商缓存原理
更新和废弃缓存
三、缓存小结
四、缓存的优点
五、浏览器缓存策略
五、缓存的应用模式
六、一些案例
参考资料
前言
缓存机制无处不在,有客户端缓存,服务端缓存,代理服务器缓存等。
一、按缓存位置分类
按缓存位置分类,可以分为:Service Worker,Memory Cache,Disk Cache。
在 Chrome 浏览器开发者工具的Network的Size栏会出现的四种情况:
- from ServiceWorker
- from memory cache
- from disk cache
- 资源本身大小(比如:13.6K)
HTTP 协议头中的缓存字段,如 Cache-Control, ETag, max-age,都属于 disk cache 的范畴。
Chrome 的隐私模式下,几乎都是 from memroy cache,一些图片是 from disk cache。
这4种情况的优先级:
- 先查找 ServiceWorker,如果 ServiceWorker 中存在,从 ServiceWorker 中加载;
- 如果 ServiceWorker 中未查找到,选择内存获取,如果内存中存在,从内存中加载;
- 如果内存中未查找到,选择硬盘获取,如果硬盘中有,从硬盘中加载;
- 如果硬盘中未查找到,那就进行网络请求;
- 加载到的资源缓存到硬盘和内存;
HTTP状态码及区别
-
200 form memory cache
不请求网络资源,资源在内存当中。浏览器关闭后,数据将被清除,再次打开相同的页面时,不会出现from memory cache。 -
200 from disk cache
不请求网络资源,在磁盘当中,关闭浏览器后,数据依然存在,此资源不会随着该页面的关闭而释放,下次打开仍然会是from disk cache。 -
304 Not Modified
请求服务端发现资源没更新,使用本地缓存资源。
几种状态的执行顺序
现加载一种资源(例如:图片):
浏览器访问 -> 200(网络请求资源) -> 退出浏览器
再进来-> 200(from disk cache) -> 刷新 -> 200(from memory cache)
Memory Cache
memory cache 叫内存缓存,几乎所有的网络请求资源都会被浏览器自动加入到 memory cache 中。而如果极端情况下 (例如一个页面的缓存就占用了超级多的内存),那可能在 TAB 没关闭之前,排在前面的缓存就已经失效了。
预加载(preload)请求过来的资源就会被放入 memory cache 中,供之后使用。
memory cache 机制保证了一个页面中如果有两个相同的请求 (例如两个 src 相同的 <img>,两个 href 相同的 <link>)都实际只会被请求最多一次,避免浪费。
不过在匹配缓存时,除了匹配完全相同的 URL 之外,还会比对他们的类型,CORS 中的域名规则等。因此一个作为脚本 (script) 类型被缓存的资源是不能用在图片 (image) 类型的请求中的,即便他们 src 相等。
如果想让 memory cache 也不存储,那就需要在 Http 请求头设置 no-store。
Disk Cache
disk cache 也叫 HTTP cache,是磁盘缓存。它允许相同的资源在跨会话,甚至跨站点的情况下使用,例如两个站点都使用了同一张图片。
disk cache 会严格根据 HTTP 头部中的各类字段来进行缓存。当命中缓存之后,浏览器会从硬盘中读取资源,虽然比起从内存中读取慢了一些,但比起网络请求还是快了不少的。绝大部分的缓存都来自 disk cache。
凡是持久性存储都会面临容量增长的问题,disk cache 也不例外。浏览器会根据自身算法自动清理“最老的”或者“最可能过时的”资源。
浏览器如何决定将资源放进内存还是硬盘呢?主要策略如下:
- 比较大的JS、CSS文件会直接被丢进磁盘,反之丢进内存
- 内存使用率比较高的时候,文件优先进入磁盘
Service Worker
上述的缓存策略以及缓存/读取/失效的动作都是由浏览器内部判断和进行的,我们只能设置响应头的某些字段来告诉浏览器,而不能自己操作。
但 Service Worker 的出现,给予了我们另外一种更加灵活,更加直接的操作方式。我们可以选择缓存哪些文件,路由匹配规则,缓存匹配并返回。
Service Worker 借鉴了 Web Worker的 思路,即让 JS 运行在主线程之外,由于它脱离了浏览器的窗体,因此无法直接访问DOM
。虽然如此,但它仍然能帮助我们完成很多有用的功能,比如离线缓存
、消息推送
和网络代理
等功能。其中的离线缓存
就是 Service Worker Cache。
Service Worker 能够操作的缓存是有别于浏览器内部的 memory cache 或者 disk cache 的。我们可以在 Chrome 开发者工具,Application -> Cache Storage 找到缓存的位置。这个缓存是永久性的,即关闭 TAB 或者浏览器,下次打开依然还在。有两种情况会导致这个缓存中的资源被清除:手动调用 API cache.delete(resource) 或者容量超过限制,被浏览器全部清空。
如果 Service Worker 没能命中缓存,一般情况会使用 fetch() 方法继续获取资源。这时候,浏览器就去 memory cache 或者 disk cache 进行下一次找缓存的工作了。
注意:经过 Service Worker 的 fetch() 方法获取的资源,即便它并没有命中 Service Worker 缓存,甚至实际走了网络请求,也会标注为 from ServiceWorker。这个情况在后面的第三个示例中有所体现。
请求网络
如果一个请求在上述 3 个位置都没有找到缓存,那么浏览器会正式发送网络请求去获取内容。为了提升之后请求的缓存命中率,会把这个资源添加到缓存中去。具体来说:
-
根据 Service Worker 中的 handler 决定是否存入 Cache Storage (额外的缓存位置)。
-
根据 HTTP 头部的相关字段(Cache-control, Pragma 等)决定是否存入 disk cache
-
memory cache 保存一份资源的引用,以备下次使用。
二、HTTP 缓存
在HTTP中具有缓存功能的是浏览器缓存。HTTP的缓存属于客户端缓存,所以我们认为浏览器存在一个缓存数据库,用于储存一些不经常变化的静态文件(图片、css、js等)。
当请求一个静态资源时,浏览器会通过以下几步来获取资源:
- 当第一次发送请求,http返回200的状态码。
- 当后续请求该文件时候,先在本地查找该资源,如果在本地缓存找到对应的资源,但是不知道该资源是否过期或者已经过期, 则发一个http请求到服务器,然后服务器判断这个请求。
- 如果请求的资源在服务器上没有改动过,则返回一个小的HTTP 304 Not Modeified响应, 让浏览器使用本地找到的那个资源。而如果请求的资源在服务器上已经修改过,或者这是一个新的请求(本地无对应资源),服务器则返回该资源的数据,并且返回200。当然这个是指找到资源的情况下,如果服务器上没有这个资源,则返回404Not Found 响应。
看了上述这个流程,相信大家对浏览器请求一个资源的流程有大致的了解了。接下来,我们来看一下具体的 HTTP 缓存。
HTTP 缓存属于 Disk Cache。
HTTP 缓存分类
HTTP 缓存分为:强缓存和协商缓存。
HTTP通过缓存将服务器文档的副本保留一段时间。在这段时间里,都认为文档时新鲜的,缓存可以在不联系服务器的情况下,直接提供该文档。我们称之为强缓存,此时浏览器会返回200状态码(from cache)。
但一旦以缓存副本停留的时间太长,超过了文档的新鲜度限值,就认为文档过期了。
再提供文档之前,缓存要再次与服务器进行再验证,已查看文档是否发生了变化。我们称之为协商缓存。
强缓存直接减少请求数,是提升最大的缓存策略。 它的优化覆盖了文章开头提到过的请求数据的全部三个步骤。如果考虑使用缓存来优化网页性能的话,强缓存应该是首先被考虑的。
强缓存原理
通过特殊的HTTP Expries
首部(HTTP/1.0时期)和 Cache-Control
首部(HTTP/1.1时期),HTTP让原始服务器向每个文档附加了一个过期日期,这些首部说明了在多长时间内可以将这些内容视为新鲜的。
浏览器第二次发送请求相同资源时,拿出过期时间和当前时间进行比较,如果在过期日期之前,则强缓存命中,如果缓存文档过期,缓存就必须与服务器进行核对,询问文档是否被修改过,如果被修改过,就要获取一份新鲜(带有新的过期日期)的副本。
强缓存首部
Expires:
在响应消息头指定一个绝对的过期日期,当再次请求时的请求时间小于返回的此时间,则直接使用缓存数据。不过由于我们可以去更改客户端的时间,因此可以更改缓存命中的结果。因此这种方式很快在后来的HTTP1.1版本中被抛弃了。故现在大多数使用Cache-Control。
Expires: Thu, 10 Nov 2017 08:45:11 GMT
如果跟 cache-control 的 max-age 同时存在,Expires 会被 max-age 覆盖。
Cache-Control:
max-age
max-age=t 表示
缓存内容将在t秒后失效(以秒为单位),在该时间内,客户端不需要向服务器发送请求。
跟 Expires 的区别就是前者是绝对时间,而后者是相对时间。如下:
Cache-Control: max-age=2592000
no-cache
和 no-store
no-cache
可以在本地和代理服务器缓存,但是这个缓存需要服务器验证才可以使用,即直接进入协商缓存阶段
。
no-store
真正意义上的“不要缓存”,不进行任何形式的缓存,每次都从服务器获取。
最佳 Cache-Control
策略:
private
客户端可以缓存,代理服务器不能缓存。这些响应通常只为单个用户缓存,因此不允许任何中间缓存对其进行缓存,例如,用户的浏览器可以缓存包含用户私人信息的HTML网页,但CDN不能缓存
public
客户端和代理服务器都可以缓存
must-revalidate
must-revalidate
告诉缓存,在事先没有跟原始服务器进行再验证的情况下,不能提供这个对象的陈旧副本,缓存仍然可以随意提供新鲜的副本。如果在缓存进行must-revalidate
新鲜度检查时,原始服务器不可用,缓存就必须返回一条504错误。
s-maxage
s-maxage是针对代理服务器的缓存时间
协商缓存原理
协商缓存,也叫对比缓存。
本地缓存过期了并不意味着他和原始服务器目前处于活跃状态的文档有实际的区别,这只是意味着到了要进行核对的时间了,这种情况被称为协商缓存,说明缓存需要询问原始服务器是否发生变化。
-
如果再验证显示内容发生了变化,缓存会获取一份新的文档副本,并将其存储在旧文档的位置上,然后将文档发送给客户端
-
如果再验证内容没有发生变化,缓存只需要获取新的首部,包括一个新的过期日期,并对缓存中的首部进行更新就行了
协商缓存在请求数上和没有缓存是一致的,但如果是 304 的话,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此在响应体体积上的节省是它的优化点。
协商缓存是可以和强制缓存一起使用的,作为在强制缓存失效后的一种后备方案。实际项目中他们也的确经常一同出现。
If-Modified-Since / Last-Modified
具体流程如下:
-
客户端第一次向服务器发起请求,服务器将最后的修改日期(
Last-Modified
)附加到所提供的资源上去 -
当再一次请求资源时,如果没有命中强缓存,在执行再验证时,会包含一个
If-Modifed-Since
首部,值为资源的 Last-Modified 日期,询问服务器该资源自从这个 Last-Modified 日期之后有没有被修改过。 -
如果内容被修改了,服务器回送新的资源,返回200状态码和最新的修改日期
-
如果内容没有被修改,会返回一个
304 Not Modified
响应
Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT
If-None-Match / ETag
有些情况下仅使用最后修改日期进行再验证是不够的:
-
有些文档有可能会被周期性的重写(比如: 从一个后台进程中写入),但实际上包含的数据常常是一样的,尽管内容没有变化,但修改日期会发生变化
-
有些文档可能被修改了,但所做修改并不重要.不需要让世界范围内的缓存都重装数据(比如填写注释)
-
有些服务器无法准确判定其页面的最后修改日期
-
有些服务器提供的文档会在毫秒间隙发生变化(比如,实时监视器),对这些服务器来说,以一秒为粒度的修改日期可能就不够用了
因此HTTP允许用户对被称为实体标签的(ETag
)的版本标识符进行比较。实体标签是附加到文档上的任意标签(引用字符串),服务器生成并返回的随机令牌通常是文件内容的哈希值或其他指纹。客户端不需要指纹是如何生成的,只需在下一次请求时将其发送至服务器。如果指纹仍然相同,则表示资源未发生变化,您就可以跳过下载。
流程和 If-Modified-Since 一致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 变成了 If-None-Match。服务器对比已缓存标签与服务器文档中的标签是否有所不同,相同返回 304, 不相同返回新资源和 200。
Etag 的优先级高于 Last-Modified。
但是实际应用中由于Etag的计算是使用算法来得出的,而算法会占用服务端计算的资源,所有服务端的资源都是宝贵的,所以就很少使用Etag了。
小贴士:
强制缓存和协商缓存机制可以同时存在,强制缓存的优先级高于协商缓存,当执行强制缓存时,如若缓存命中,则直接使用缓存数据库数据,不在进行缓存协商。
更新和废弃缓存
浏览器发出的所有HTTP请求会首先路由到浏览器缓存,已确认是否缓存可用于请求的有效响应。如果有匹配的响应,则从缓存中读取响应,这样就避免了网路延迟和传送产生的流量费用。
不过如果我们想更新或废弃缓存的响应,该怎么办?例如我们有一个css样式表缓存长达24小时,但是我们需要立即更新他,我们如何通知已过时的CSS缓存副本的所有访问者更新其缓存。在不更改资源网址的情况下,是做不到的。
所以,如何才能实现客户端缓存和快速更新,你可以在资源内容发生变化时,更改它的网址,强制用户下载新响应。通常情况下,可以通过在文件名中嵌入文件的指纹或版本号来实现。
-
HTML被标记为
no-cache
,这意味着浏览器再每次请求时都始终重新验证文档,并在内容变化时获取最新版本。此外再HTML标记内,再CSS和javascript中嵌入指纹,如果这些文件的内容发生变化,网页的HTML也会随之改变,并会下载HTML响应的新副本 -
允许浏览器和中间缓存(例如CDN)缓存CSS,并将CSS设置为1年后到期,因为再文件名中嵌入了文件的指纹,CSS更新时网址也会随之变化
-
JavaScript同样设置为1年后到期,但标记为
private
,这或许是因为它包含的某些用户私人数据是CDN不应缓存的。 -
图像缓存时不包含版本或唯一指纹,并设置为一天后到期
三、缓存小结
浏览器请求一个静态资源的完整流程:
-
调用 Service Worker 的 fetch 事件响应
-
查看 memory cache
-
查看 disk cache。这里又细分:
-
如果有强制缓存且未失效,则使用强制缓存,不请求服务器。这时的状态码全部是 200
-
如果有强制缓存但已失效,使用协商缓存,比较后确定 304 还是 200
-
-
发送网络请求,等待网络响应
-
把响应内容存入 disk cache (如果 HTTP 头信息配置可以存的话)
-
把响应内容的引用存入 memory cache (无视 HTTP 头信息的配置)
-
把响应内容存入 Service Worker 的 Cache Storage (如果 Service Worker 的脚本调用了 cache.put())
四、缓存的优点
- 减少了冗余的数据传递,节省宽带流量
- 减少了服务器的负担,大大提高了网站性能
- 加快了客户端加载网页的速度,这也正是HTTP缓存属于客户端缓存的原因。
五、浏览器缓存策略
默认情况下,浏览器都会使用缓存数据。
用户在浏览器操作时,会触发怎样的缓存策略。主要有 3 种:
-
浏览器地址栏输入url 回车: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
-
普通刷新 (Windows: F5。Mac: Cmd+R):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache。
-
强制刷新 (Windows: Ctrl + F5。Mac: Cmd+Shift+R):会去服务器请求最新的资源文件下来。于是客户端就完成了强行更新的操作。
五、缓存的应用模式
了解了缓存的原理,我们可能更加关心如何在实际项目中使用它们,才能更好的让用户缩短加载时间,节约流量等。这里有几个常用的模式,供大家参考。
模式 1:不常变化的资源
Cache-Control: max-age=31536000
通常在处理这类资源资源时,给它们的 Cache-Control 配置一个很大的 max-age=31536000 (一年),这样浏览器之后请求相同的 URL 会命中强制缓存。而为了解决更新的问题,就需要在文件名(或者路径)中添加 hash, 版本号等动态字符,之后更改动态字符,达到更改引用 URL 的目的,从而让之前的强制缓存失效 (其实并未立即失效,只是不再使用了而已)。
在线提供的类库 (如 jquery-3.3.1.min.js, lodash.min.js 等) 均采用这个模式。如果配置中还增加 public 的话,CDN 也可以缓存起来,效果拔群。
这个模式的一个变体是在引用 URL 后面添加参数 (例如 ?v=xxx 或者 ?_=xxx),这样就不必在文件名或者路径中包含动态参数,满足某些完美主义者的喜好。在项目每次构建时,更新额外的参数 (例如设置为构建时的当前时间),则能保证每次构建后总能让浏览器请求最新的内容。
特别注意: 在处理 Service Worker 时,对待 sw-register.js(注册 Service Worker) 和 serviceWorker.js (Service Worker 本身) 需要格外的谨慎。如果这两个文件也使用这种模式,你必须多多考虑日后可能的更新及对策。
模式 2:经常变化的资源
Cache-Control: no-cache
这里的资源不单单指静态资源,也可能是网页资源,例如博客文章。这类资源的特点是:URL 不能变化,但内容可以(且经常)变化。我们可以设置 Cache-Control: no-cache 来迫使浏览器每次请求都必须找服务器验证资源是否有效。
既然提到了验证,就必须 ETag 或者 Last-Modified 出场。这些字段都会由专门处理静态资源的常用类库(例如 koa-static)自动添加,无需开发者过多关心。
也正如上文中提到协商缓存那样,这种模式下,节省的并不是请求数,而是请求体的大小。所以它的优化效果不如模式 1 来的显著。
模式 3:非常危险的模式 1 和 2 的结合 (反例)
Cache-Control: max-age=600, must-revalidate
不知道是否有开发者从模式 1 和 2 获得一些启发:模式 2 中,设置了 no-cache,相当于 max-age=0, must-revalidate。我的应用时效性没有那么强,但又不想做过于长久的强制缓存,我能不能配置例如 max-age=600, must-revalidate 这样折中的设置呢?
表面上看这很美好:资源可以缓存 10 分钟,10 分钟内读取缓存,10 分钟后和服务器进行一次验证,集两种模式之大成,但实际线上暗存风险。因为上面提过,浏览器的缓存有自动清理机制,开发者并不能控制。
举个例子:当我们有 3 种资源: index.html, index.js, index.css。我们对这 3 者进行上述配置之后,假设在某次访问时,index.js 已经被缓存清理而不存在,但 index.html, index.css 仍然存在于缓存中。这时候浏览器会向服务器请求新的 index.js,然后配上老的 index.html, index.css 展现给用户。这其中的风险显而易见:不同版本的资源组合在一起,报错是极有可能的结局。
除了自动清理引发问题,不同资源的请求时间不同也能导致问题。例如 A 页面请求的是 A.js 和 all.css,而 B 页面是 B.js 和 all.css。如果我们以 A -> B 的顺序访问页面,势必导致 all.css 的缓存时间早于 B.js。那么以后访问 B 页面就同样存在资源版本失配的隐患。
六、一些案例
光看原理不免枯燥。我们编写一些简单的网页,通过案例来深刻理解上面的那些原理。
memory cache & disk cache
我们写一个简单的 index.html,然后引用 3 种资源,分别是 index.js, index.css 和 mashroom.jpg。
我们给这三种资源都设置上 Cache-control: max-age=86400,表示强制缓存 24 小时。以下截图全部使用 Chrome 的隐身模式。
首次请求
毫无意外的全部走网络请求,因为什么缓存都还没有。
再次请求 (F5)
第二次请求,三个请求都来自 memory cache。因为我们没有关闭 TAB,所以浏览器把缓存的应用加到了 memory cache。(耗时 0ms,也就是 1ms 以内)
关闭 TAB,打开新 TAB 并再次请求
因为关闭了 TAB,memory cache 也随之清空。但是 disk cache 是持久的,于是所有资源来自 disk cache。(大约耗时 3ms,因为文件有点小)
而且对比 2 和 3,很明显看到 memory cache 还是比 disk cache 快得多的。
no-cache & no-store
我们在 index.html 里面一些代码,完成两个目标:
-
每种资源都(同步)请求两次
-
增加脚本异步请求图片
<!-- 把3种资源都改成请求两次 -->
<link rel="stylesheet" href="/static/index.css">
<link rel="stylesheet" href="/static/index.css">
<script src="/static/index.js"></script>
<script src="/static/index.js"></script>
<img src="/static/mashroom.jpg">
<img src="/static/mashroom.jpg">
<!-- 异步请求图片 -->
<script>setTimeout(function () {let img = document.createElement('img')img.src = '/static/mashroom.jpg'document.body.appendChild(img)}, 1000)
</script>
当把服务器响应设置为 Cache-Control: no-cache 时,我们发现打开页面之后,三种资源都只被请求 1 次。
这说明两个问题:
-
同步请求方面,浏览器会自动把当次 HTML 中的资源存入到缓存 (memory cache),这样碰到相同 src 的图片就会自动读取缓存(但不会在 Network 中显示出来)
-
异步请求方面,浏览器同样是不发请求而直接读取缓存返回。但同样不会在 Network 中显示。
总体来说,如上面原理所述,no-cache 从语义上表示下次请求不要直接使用缓存而需要比对,并不对本次请求进行限制。因此浏览器在处理当前页面时,可以放心使用缓存。
当把服务器响应设置为 Cache-Control: no-store 时,情况发生了变化,三种资源都被请求了 2 次。而图片因为还多一次异步请求,总计 3 次。(红框中的都是那一次异步请求)
这同样说明:
-
如之前原理所述,虽然 memory cache 是无视 HTTP 头信息的,但是 no-store 是特别的。在这个设置下,memory cache 也不得不每次都请求资源。
-
异步请求和同步遵循相同的规则,在 no-store 情况下,依然是每次都发送请求,不进行任何缓存。
Service Worker & memory (disk) cache
我们尝试把 Service Worker 也加入进去。我们编写一个 serviceWorker.js,并编写如下内容:(主要是预缓存 3 个资源,并在实际请求时匹配缓存并返回)
// serviceWorker.js
self.addEventListener('install', e => {// 当确定要访问某些资源时,提前请求并添加到缓存中。// 这个模式叫做“预缓存”e.waitUntil(caches.open('service-worker-test-precache').then(cache => {return cache.addAll(['/static/index.js', '/static/index.css', '/static/mashroom.jpg'])}))
})
self.addEventListener('fetch', e => {// 缓存中能找到就返回,找不到就网络请求,之后再写入缓存并返回。// 这个称为 CacheFirst 的缓存策略。return e.respondWith(caches.open('service-worker-test-precache').then(cache => {return cache.match(e.request).then(matchedResponse => {return matchedResponse || fetch(e.request).then(fetchedResponse => {cache.put(e.request, fetchedResponse.clone())return fetchedResponse})})}))
})
注册 SW 的代码这里就不赘述了。此外我们还给服务器设置 Cache-Control: max-age=86400 来开启 disk cache。我们的目的是看看两者的优先级。
当我们首次访问时,会看到常规请求之外,浏览器(确切地说是 Service Worker)额外发出了 3 个请求。这来自预缓存的代码。
第二次访问(无论关闭 TAB 重新打开,还是直接按 F5 刷新)都能看到所有的请求标记为 from SerciceWorker。
from ServiceWorker 只表示请求通过了 Service Worker,至于到底是命中了缓存,还是继续 fetch() 方法光看这一条记录其实无从知晓。因此我们还得配合后续的 Network 记录来看。因为之后没有额外的请求了,因此判定是命中了缓存。
从服务器的日志也能很明显地看到,3 个资源都没有被重新请求,即命中了 Service Worker 内部的缓存。
如果修改 serviceWorker.js 的 fetch 事件监听代码,改为如下:
// 这个也叫做 NetworkOnly 的缓存策略。
self.addEventListener('fetch', e => {return e.respondWith(fetch(e.request))
})
可以发现在后续访问时的效果和修改前是完全一致的。(即 Network 仅有标记为 from ServiceWorker 的几个请求,而服务器也不打印 3 个资源的访问日志)
很明显 Service Worker 这层并没有去读取自己的缓存,而是直接使用 fetch() 进行请求。所以此时其实是 Cache-Control: max-age=86400 的设置起了作用,也就是 memory/disk cache。但具体是 memory 还是 disk 这个只有浏览器自己知道了,因为它并没有显式的告诉我们。(个人猜测是 memory,因为不论从耗时 0ms 还是从不关闭 TAB 来看,都更像是 memory cache)
浏览器缓存场景解析
普通刷新 command + R:使用缓存资源
网页账号退出重新登录:使用缓存资源
强制刷新 shift + command + R:缓存清除不干净,部分用最新资源,部分用缓存
彻底清除缓存方法
开启Chrome devtool的“停用缓存”:缓存清除彻底,全部重新加载最新资源
使用浏览器的“清空缓存并硬性重新加载”:缓存清除彻底,全部重新加载最新资源
要先开启 Chrome 的 devtool,然后右键导航栏的转圈 icon。
使用浏览器的“清除浏览数据”:缓存清除彻底,全部重新加载最新资源
参考资料
- 一文读懂前端缓存
- from memory cache与from disk cache
- 你应该知道的前端——缓存