While ServiceWorkers and PWAs are becoming the standard for modern web applications, browser resource caching has become more complex than ever.
This article covers the highlights of browser caching, including:
- ServiceWorker cache vs HTTP cache priority?
- Where are the MemoryCache and DiskCache implemented by mainstream browsers?
- Which is faster, MemoryCache, DiskCache, or ServiceWorker?
Overview of the caching process
Let's first look at the sequence of resource requests defined by the standard:
- ServiceWorker caching : The ServiceWorker checks if the resource exists in its cache and decides whether to return the resource based on its programmed caching strategy. This operation will not happen automatically. It needs to be defined in the registered ServiceWorker
fetch
event to intercept and process network requests, so that the ServiceWorker cache can be hit instead of the network or HTTP cache. - HTTP cache : This is what we often call "strong cache" and "negotiated cache". If the HTTP cache has not expired, the browser will use the HTTP cached resources.
- Server side : If no resource is found in the ServiceWorker cache or HTTP cache, the browser requests the resource from the network. This will involve the work of CDN services or source services.
This is the resource request process defined by the standard, but the browsers who are pursuing will also add a "memory cache layer" on top of the ServiceWorker. Taking Chrome as an example, we request a resource, except for the network, there will be three types of browser caches to return:
So what is the priority of MemoryCache, DiskCache and ServiceWorker Cache?
Let's talk about the difference between the three.
In which layer of the cache process are MemoryCache and DiskCache?
Let's take Chrome as an example, MemoryCache is the first citizen, located above ServiceWorker.
That is, if the MemoryCache is hit, the fetch event of the ServiceWorker will not be triggered.
And DiskCache is located in the original HTTP cache layer:
The existence of MemoryCache causes a problem: ServiceWorker does not always have control over resources.
This makes the situation more complex and unpredictable than we would have expected. Unfortunately, MemoryCache is not in the W3C standard. W3C is still discussing this matter since 2016. It seems that this problem cannot be solved in a short time.
Some topics under discussion:
Is there really nothing we can do?
If we encounter business scenarios that do have strong requirements for the control of ServiceWorker resources, we can still do something.
MemoryCache is controlled by "strong caching" , which means that we can intercept resource responses in ServiceWorker and set resource response headers to invalidate resources from 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'
}});
})();
);
});
It should be noted that this approach is premised on sacrificing a small amount of load performance. It depends on our actual scenario is performance priority, or offline priority, or what else is the priority.
Which is faster, MemoryCache, DiskCache, or ServiceWorker?
Let's take a look at the loading speed and priority of the three caches of the same resource:
- Loading speed: MemoryCache > DiskCache > ServiceWorker
- Priority: MemoryCache > ServiceWorker > DiskCache
The priority of MemoryCache is in front of ServiceWorker, this is no problem.
But the slower ServiceWorker has higher priority than the faster DiskCache?
After that, isn't ServiceWorker slowing down the loading speed of the site?
Controlled experiment
To investigate this question, I did a set of controlled experiments.
Experiments are only done in Chrome, the chrome devtool provides time for each resource. All loaded resource information can be downloaded as a HAR file, and then a local script can be written for information extraction and analysis.
Experimental conditions
- Same environment: Chrome97 / MacOS 10.14 / Wifi
Multiple concurrent loads of the same image:
- 3 133KB pictures 10 experiments
- 10 133KB pictures 10 experiments
- 100 133KB pictures 10 experiments
Observe two properties:
- DiskCache cache performance
- ServiceWorker cache speed performance
Experiment 1: Three 133KB images concurrently
The first is to concurrently request 3 images for 10 experiments, take the average data, and then observe the performance of DiskCache and ServiceWorker Cache respectively.
observe:
- DiskCache: We found that the download operation did not take much time, but the resources were waiting to be queued.
- ServiceWorker Cache: More time-consuming to download.
Conclusion: But despite this, DiskCache is still faster than ServiceWorker Cache in this case.
Experiment 2: 3 133KB pictures 10 experiments
When I increase the number of concurrent images to 10, the situation may be closer to the actual situation, and the site may have more different resources (JS files, fonts, styles, images, etc.), because some sites may There are more than 10 resources on a page.
observe:
- DiskCache: The queue time from the second resource is still very long, but the download time is basically unchanged.
- ServiceWorker Cache: Queuing is not a problem, but waiting is.
Conclusion: In this case, DiskCache will be slightly inferior to ServiceWorker Cache.
Experiment 3: 100 experiments for 3 133KB pictures
When I increased the concurrency to 100 images, the situation was almost unreal, but I wondered why DiskCache was faster than ServiceWorker Cache in the first trial.
observe:
- DiskCache: Queuing is still a problem and increases linearly with the number of concurrency. We can even see how the browser loads the images, about 6 images at a time.
- ServiceWorker Cache: Although the waiting time increases with the number of concurrency, it is flat.
Conclusion: ServiceWorker Cache is faster than DiskCache under large concurrency.
How to choose DiskCache and ServiceWorker?
Only children make choices, adults do
Since the priority of ServiceWorker is above DiskCache, we can perform "resource racing" in ServiceWorker, requesting ServiceWorker Cache and DiskCache at the same time, whichever returns first will return the resource to the previous layer. The code might look like this:
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)
])
})();
);
});
Experiment 4: After resource racing, request 3 images, 10 images and 100 images concurrently
When we do resource racing, in this case, whether it is a small amount of resources or a large amount of resources concurrently, we can reach the fastest level.
Summarize
In this article, we understand the priorities of ServiceCache, MemoryCache, and DiskCache.
Then in-depth comparison of the performance of ServiceWorker Cache and DiskCache.
DiskCache is faster when a small number of resources are concurrent, and ServiceWorker is faster when a large number of resources are concurrent.
Finally, the "resource racing" method is used to balance the two situations.
However, at some point, our comparison of ServiceWorker and HTTP caching is a bit unfair.
ServiceWorker will be more widely used, it provides more fine-grained cache control, enables offline applications, and can use more CacheAPI capacity than the main thread.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。