尽管 ServiceWorker 和 PWA 正在成为古代 Web 应用程序的规范,但浏览器资源缓存变得比以往任何时候都简单。
本文涵盖了浏览器缓存的重点内容,具体包含:
- ServiceWorker 缓存与 HTTP 缓存的优先级?
- 支流浏览器实现的 MemoryCache 和 DiskCache 在哪一层?
- MemoryCache、DiskCache、ServiceWorker 缓存哪个速度更快?
缓存流程概述
咱们先来看规范定义的资源申请遵循的程序:
- ServiceWorker 缓存 :ServiceWorker 查看资源是否存在其缓存中,并依据其编程的缓存策略决定是否返回资源。这个操作不会主动产生,须要在注册的 ServiceWorker 中定义
fetch
事件去拦挡并解决网络申请,这样能力命中 ServiceWorker 缓存而不是网络或者 HTTP 缓存。 - HTTP 缓存 :这里就是咱们经常说的「强缓存」和「协商缓存」,如果 HTTP 缓存未过期的话,浏览器就会应用 HTTP 缓存的资源。
- 服务器端 :如果 ServiceWorker 缓存或者 HTTP 缓存中未找到任何资源,则浏览器会向网络申请资源。这里就会波及到 CDN 服务或者源服务的工作了。
这是规范定义的资源申请流程,然而有谋求的浏览器还会在 ServiceWorker 下面加一层 「内存缓存层」,以 Chrome 为例,咱们申请一个资源,除去网络,会有三种浏览器缓存返回:
那么 MemoryCache 和 DiskCache 与 ServiceWorker Cache 的优先级是怎么样的呢?
上面咱们讲讲三者的区别。
MemoryCache、DiskCache 在缓存流程的哪一层?
咱们以 Chrome 为例,MemoryCache 作为第一公民,位于 ServiceWorker 之上。
也就是命中了 MemoryCache,就不会触发 ServiceWorker 的 fetch 事件。
而 DiskCache 则位于原来的 HTTP 缓存层:
MemoryCache 的存在会导致一个问题:ServiceWorker 并不总是对资源有着控制权。
这会另咱们原本冀望的状况会变得复杂且不可预知。惋惜的是 MemoryCache 并不在 W3C 的规范中,W3C 从 2016 年到当初依然在探讨着这个事件,看来短时间这个问题是得不到解决了。
一些正在探讨的话题:
- safari fetches from memory cache instead of Service worker
- Difference between disk and memory cache
- Advanced Questions About Service Worker
- allow service worker produced resources to be marked as “cachable”
咱们真的没有方法么?
要是咱们遇到业务场景,的确对 ServiceWorker 资源控制权有很强的的要求,咱们还是能够做点事件的。
MemoryCache 是受控于 「强缓存」 的,这意味着咱们能够在 ServiceWorker 拦挡资源的响应,并设置资源响应头来使资源从 MemoryCache 生效:
cache-control: max-age=0
self.addEventListener("fetch", (event) => {
event.respondWith((async function () {
// 从 HTTP 缓存或者网络获取资源
const res = fetch(event.request);
// 因为 Response 是一个流,只能用一次,所以这里要 clone 一下。const newRes = res.clone();
// 改写资源响应头
return new Response(res.body, { ...newRes, headers: {'cache-control': 'max-age=0'}});
})(););
});
须要留神的是,这种办法是以就义大量加载性能为前提的。这取决于咱们理论场景中是性能优先,还是离线优先,或者其余什么状况优先。
MemoryCache、DiskCache、ServiceWorker 缓存哪个速度更快?
咱们再看一下同一个资源三种缓存的加载速度和优先级:
- 加载速度:MemoryCache > DiskCache > ServiceWorker
- 优先级:MemoryCache > ServiceWorker> DiskCache
MemoryCache 优先级在 ServiceWorker 后面,这个没问题。
然而速度更慢的 ServiceWorker 优先级比速度更快的 DiskCache 更高?
那盘下来,ServiceWorker 岂不是减慢了站点的加载速度?
对照试验
为了钻研这个问题,我做了一组对照试验。
试验只在 Chrome 进行,chrome devtool 为每个资源提供工夫。所有加载资源的信息都能够作为 HAR 文件下载下来,而后编写本地脚本进行信息提取和剖析。
试验条件
- 同一个环境:Chrome97 / MacOS 10.14 / Wifi
-
同一张图片的屡次并发加载:
- 3 张 133KB 图片 10 次试验
- 10 张 133KB 图片 10 次试验
- 100 张 133KB 图片 10 次试验
-
察看两个性能:
- DiskCache 缓存性能体现
- ServiceWorker 缓存速度体现
试验一:3 张 133KB 图片并发
首先是并发申请 3 张图片进行 10 次试验,取均匀数据,而后别离察看 DiskCache、ServiceWorker Cache 的性能体现。
察看:
- DiskCache:咱们发现下载操作并没有花太多工夫,然而资源在期待排队。
- ServiceWorker Cache:更多耗时在下载。
论断:但尽管如此,这种状况下,DiskCache 仍然是比 ServiceWorker Cache 更快。
试验二:3 张 133KB 图片 10 次试验
当我把并发图片减少到 10 张,这种状况可能会更加靠近于理论状况,站点中可能会领有更多的不同的资源(JS 文件、字体、款式、图像等),因为某些网站可能会在一个页面存在超过 10 个资源。
察看:
- DiskCache:从第二个资源开始排队工夫仍然很长,然而下载工夫是根本不变的。
- ServiceWorker Cache:排队并不是问题,但期待是。
论断:这种状况下,DiskCache 会略逊于 ServiceWorker Cache。
试验三:3 张 133KB 图片 100 次试验
当我把并发图片减少到 100 张,这种状况简直是不实在的状况,然而我好奇为什么 DiskCache 为什么在第一次试验中比 ServiceWorker Cache 更快。
察看:
- DiskCache:排队仍然是问题,且随着并发数成线性回升。咱们甚至能看到浏览器是如何加载图片的,一次并发大略 6 张图片。
- ServiceWorker Cache:尽管等待时间随着并发数回升,然而是平缓的。
论断: 大并发下 ServiceWorker Cache 比 DiskCache 更快。
那 DiskCache 和 ServiceWorker 怎么抉择?
小孩子才做抉择,小孩儿都要
因为 ServiceWorker 的优先级在 DiskCache 之上,咱们能够在 ServiceWorker 进行 「资源竞速」,同一时间申请 ServiceWorker Cache 和 DiskCache,哪个先返回就把资源返回上一层。代码可能是这样的:
self.addEventListener("fetch", (event) => {
event.respondWith((async function () {
const res = Promise.race([
// 申请 ServiceWorker Cache
cache.open(CACHE_NAME).then(cache => cache.match(event.request)),
// 申请 DiskCache 或者网络资源
fetch(event.request)
])
})(););
});
试验四:资源竞速之后,并发申请 3 张图片、10 张图片 和 100 张图片
当咱们进行资源竞速之后,这种状况下,无论是并发大量资源还是大量资源,都能达到最快的级别。
总结
本篇咱们搞懂了 ServiceCache、MemoryCache、DiskCache 的优先级。
而后深刻比照了 ServiceWorker Cache 和 DiskCache 的性能体现。
在大量资源并发的时候,DiskCache 更快,在大量资源并发的时候,ServiceWorker 更快。
最初通过「资源竞速」的形式来兼顾两种状况。
然而,在某些时候,咱们比拟 ServiceWorker 和 HTTP 缓存有点不偏心。
ServiceWorker 的用处会更加宽泛,它提供了更细力度的缓存管制、使离线化利用得以实现、并且比照主线程,他可能应用更多的 CacheAPI 容量。