Service Workers 缓存 API 与 Fetch 事件解析 - Francisco Brusa
Service Workers 缓存 API 和 Fetch 事件解析
Service Worker 是一种运行在独立于浏览器主线程的环境中的脚本。它的特点是每个域只运行一次,而不是每个浏览器选项卡都运行一次。当页面刷新时,如果没有加载特定的脚本标记,它不会再次执行。例如,以下代码可以注册一个 Service Worker:
navigator.serviceWorker.register("/serviceWorker.js");
Service Worker 的请求拦截能力
Service Worker 可以拦截任何出站网络请求,无论是浏览器尝试从其他域获取资源,还是在离线状态下发起请求。这包括通过 GET 或 POST 方法获取的脚本、图像或 HTML 文档等资源。
在拦截请求时,您可以对响应进行多种操作,例如:
- 发起实际的网络请求并将响应存储到缓存中。
- 从缓存中检索之前的响应版本。
- 修改响应的内容、标题或状态码(尽管不建议这样做)。
缓存 API(WindowOrWorkerGlobalScope.caches)
缓存 API 是一个基于请求/响应键值对的存储机制。虽然大多数示例都展示了如何在 Service Worker 中使用缓存 API(如 caches.open()),但实际上它也可以在普通的 JavaScript 环境中使用。
缓存 API 的基本用法
为了更好地理解缓存 API,我们可以从以下几个方面入手:
-
Request 类与 fetch 函数的结合
Request 类与 fetch 函数的签名一致,因此可以直接使用 Request 对象调用 fetch 函数。此外,您可以使用 Request 和 Response 对象向缓存中添加条目。
-
从缓存中检索条目
您可以通过将缓存条目与类似的 Request 对象进行匹配,从缓存中检索相应的条目。值得注意的是,缓存 API 会自动处理请求对象的序列化问题。
-
浏览器环境中的使用
缓存 API 不仅限于 Service Worker 环境,也可以在支持 fetch 函数的浏览器环境中使用。
以下是一个在浏览器和 Service Worker 中都有效的函数示例,该函数实现了 Workbox 中的“缓存优先”策略:
async function fetchUsingCacheFirstStrategy(request) {
const cache = await caches.open('my-cache');
const cachedResponse = await cache.match(request);
if (cachedResponse) {
return cachedResponse;
}
const networkResponse = await fetch(request);
await cache.put(request, networkResponse.clone());
return networkResponse;
}
需要注意的是,上述示例未考虑缓存失效问题,因此不适合直接用于生产环境。
Fetch 事件与缓存策略
Service Worker 的强大之处在于它可以通过 self.addEventListener("fetch") 监听 FetchEvent 类型的事件。这种方式不仅适用于 JavaScript 发起的 XHR 请求,还可以对浏览器加载脚本、样式表、图像等资源的请求应用缓存策略。
Fetch 事件的触发条件
Fetch 事件会在浏览器发出的任何请求时触发,包括:
- 使用
fetch()发起的请求。 - 通过 XMLHttpRequest 发起的请求。
- 由 HTML 标签(如
<img>、<link>和<svg>)触发的请求。
在监听 Fetch 事件时,实际的请求会被拦截,只有在明确允许的情况下才会发出。这意味着您可以完全控制请求的执行。例如,以下代码为所有出站请求添加了一个自定义标头:
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request, {
headers: {
'Custom-Header': 'MyHeaderValue'
}
})
);
});
缓存优先策略的应用
结合缓存 API,可以使用缓存优先策略处理特定类型的请求。例如,以下代码为所有图像请求应用缓存优先策略:
self.addEventListener('fetch', event => {
if (event.request.destination === 'image') {
event.respondWith(
caches.open('image-cache').then(cache =>
cache.match(event.request).then(cachedResponse =>
cachedResponse || fetch(event.request).then(networkResponse => { cache.put(event.request, networkResponse.clone());
return networkResponse;
})
)
)
);
}
});
结论
缓存 API 是一个强大的工具,可以在浏览器和 Service Worker 中使用。然而,只有在 Service Worker 中,您才能完全控制非 XHR 请求(如图像、样式表等)的处理方式。
这意味着,您可以先在浏览器中使用缓存 API 处理部分 API 调用,然后在需要缓存非 API 调用(或提供离线支持)时,将其集成到 Service Worker 中,从而实现更高级的功能。
原文链接: https://www.franciscobrusa.dev/blog/service-workers-cache