返回
创建于
状态
公开
要实现 Service Worker 中的 Cache Storage 与原生浏览器缓存(Disk Cache/HTTP Cache)“竞速”,核心矛盾在于:Service Worker 本身是拦截器。
默认情况下,当你调用 fetch(event.request) 时,浏览器会先经过 Service Worker 的逻辑,再去尝试它的原生缓存策略。如果你想让两者真正“竞速”,通常是指**“缓存优先,但如果缓存响应太慢,就尽快使用网络(可能命中 Disk Cache)的结果”**。
不过,从底层机制上看,caches.match(Cache Storage)和 fetch(Network/Disk Cache)是并行的。你可以通过 Promise.race 来实现这个逻辑:
竞速模式代码实现
你可以将逻辑重构为如下形式,让缓存和网络同时启动,谁先返回有效结果就用谁:
1globalSelf.addEventListener('fetch', (event) => {
2 const url = new URL(event.request.url);
3
4 if (url.origin === 'https://help.littleeleven.com' && event.request.method === 'GET') {
5 event.respondWith(
6 (async () => {
7 const cache = await caches.open(HELP_CENTER_CACHE_NAME);
8
9 // 创建两个 Promise 进行竞速
10 const cachePromise = cache.match(event.request);
11 const networkPromise = fetch(event.request);
12
13 try {
14 // 使用 Promise.race 竞速
15 // 注意:通常我们需要对 Race 结果进行筛选,因为 cache.match 没命中会返回 undefined
16 const response = await Promise.race([
17 cachePromise.then(res => {
18 if (res) return res;
19 // 如果缓存没中,返回一个永远 pending 的 promise,把机会让给网络
20 return new Promise(() => {});
21 }),
22 networkPromise.then(networkRes => {
23 if (networkRes.ok) {
24 cache.put(event.request, networkRes.clone());
25 }
26 return networkRes;
27 })
28 ]);
29
30 return response;
31 } catch (error) {
32 // 如果两者都失败(或网络断开且缓存无数据)
33 const cachedResponse = await cachePromise;
34 if (cachedResponse) return cachedResponse;
35
36 return new Response('Network error and no cache', {
37 status: 408,
38 headers: { 'Content-Type': 'text/plain' }
39 });
40 }
41 })()
42 );
43 }
44});为什么会出现“Cache 慢于 Disk Cache”?
虽然理论上 Cache Storage (IndexedDB 背后) 应该很快,但在某些极端情况下(I/O 繁忙或 SW 唤醒延迟),它确实可能表现不佳。
- V8 序列化开销:从 Cache Storage 读取大的二进制文件需要经过 IPC 和序列化。
- 浏览器优化:Disk Cache 是内核级别的逻辑,路径更短。
竞速策略建议
如果你非常在意极致性能,建议采用 Stale-While-Revalidate (SWR) 模式:
- 立即返回缓存(即使它可能比 Disk Cache 慢一点点,但绝大多数情况它依然是最快的)。
- 后台静默更新:无论缓存是否命中,都在后台发起
fetch更新 Cache Storage。
关键注意事项
- 重复请求:
Promise.race会导致网络请求总是发出,这会增加用户的流量消耗。 - AbortController:如果你选择了其中一个结果,理想情况下应该取消另一个正在进行的请求(特别是网络请求),但在 Service Worker 中,简单地忽略
Promise结果通常比取消请求更易于维护。