Service Worker 需手动注册、配置缓存策略并劫持请求才能实现离线功能,必须通过 HTTPS(或 localhost)注册,install 预缓存静态资源,fetch 中主动匹配缓存,配合 skipWaiting() 和 clients.claim() 确保新版本立即生效。
Service Worker 是浏览器中运行在独立线程里的脚本,它不绑定具体页面,能拦截、修改、响应网络请求,并支持后台消息、推送和离线缓存——但它不是“自动让网站离线”的开关,必须手动注册 + 缓存策略 + 请求劫持三者配合才能生效。
这是硬性安全限制,生产环境 HTTP 域名下 navigator.serviceWorker.register() 会直接失败,控制台报错 Failed to register a ServiceWorker: Scheme 'http' is not supported。开发时可用 localhost 绕过,但 127.0.0.1 或自定义 hosts 域名(如 dev.local)不行,除非配了 HTTPS。
npx http-s
erver -S 启用 HTTPSStrict-Transport-Security 头没误配导致 HSTS 强制跳转失败document.addEventListener('DOMContentLoaded', ...) 里——因为 SW 注册是同步发起的,等 DOM 就晚了install 和 fetch 事件协同完成只在 install 里调用 caches.open().then(cache => cache.addAll([...])) 只是预加载静态资源;真正让页面离线可用,得在 fetch 事件里主动从缓存读取,否则浏览器仍会发网络请求并失败。
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
// 缓存命中:返回缓存
if (response) return response;
// 缓存未命中:发网络请求,并顺手存入缓存(可选)
return fetch(event.request).then(networkResponse => {
if (networkResponse && networkResponse.status === 200) {
const copy = networkResponse.clone();
caches.open('v1').then(cache => cache.put(event.request, copy));
}
return networkResponse;
});
})
);
});
caches.match() 匹配的是完整 URL(含 query 参数),/index.html?v=2 和 /index.html 是两个不同缓存项/api/user)默认不被 cache.addAll() 包含,需在 fetch 中按需缓存,注意设置合理 Cache-Control 或手动加 TTL 判断fetch 里无条件 return fetch(...),那等于没走缓存skipWaiting() 和 clients.claim() 决定新 SW 何时生效默认情况下,已打开的页面仍使用旧版 SW,即使新版本已安装完成。用户必须刷新两次才能用上新版逻辑——第一次触发 install,第二次才让新 SW 控制页面。
install 事件末尾加 self.skipWaiting(),跳过 waiting 状态activate 事件里调用 self.clients.claim(),让新 SW 立即接管所有当前客户端(包括已打开的 tab)最易被忽略的一点:Service Worker 的生命周期由浏览器严格管理,它可能随时被终止(比如内存紧张时),所以不能依赖全局变量存状态,所有关键数据必须写入 IndexedDB 或 caches;另外,console.log 在 SW 里默认不输出到页面控制台,得打开 Chrome 的 Application → Service Workers 面板勾选 “Update on reload” 并看专用日志。