如何利用 Service Worker 拦截全局请求实现静态资源的零延迟更新需要先看清适用场景和关键步骤,避免只记结论却忽略实际限制。
Service Worker 无法实现静态资源零延迟更新,因 fetch 拦截有开销且需安装完成、页面重载或 clients.claim() 接管;真正目标是更新对用户不可见且下次访问即用新版。
不能。所谓“零延迟”是误解——fetch 事件拦截本身有微小开销,且更新生效必须等待新 Service Worker 安装完成、页面重新加载或 clients.claim() 主动接管,用户首次访问仍走网络。真正能做的是:让更新对用户不可见 + 下次访问立即用新版。
关键不在拦截动作多快,而在如何避免“旧 SW 锁住旧资源”和“新资源已发布但未生效”的错位。
常见错误是把 Service Worker 注册在 /admin/ 下却想拦截 /static/js/app.js——注册路径决定可控制范围,必须注册在站点根路径(如 /sw.js)才能覆盖全部静态资源。
script 中,且确保执行时机早于任何资源请求(建议放在 <head> 底部)navigator.serviceWorker.register('/sw.js') 返回的 Promise 是否 resolve,reject 时打印 err.message,常见原因是 HTTPS 缺失或路径 404https://example.com/myapp/),注册时需显式传入 { scope: '/myapp/' },否则默认 scope 是文件所在目录,无法控制上级路径盲目拦截所有 fetch 请求再走缓存,会导致 API 接口也被缓存,引发数据陈旧问题。必须按 URL 特征或请求头精准识别静态资源。
推荐判断逻辑(在 self.addEventListener('fetch', ...) 内):
const isStaticResource = (url) => { return url.pathname.match(/.(js|css|png|jpg|gif|svg|woff2?|ttf|eot|ico|webp)$/i) || url.pathname.startsWith('/static/') || url.pathname.startsWith('/dist/');};// 注意:不要用 location.origin 判断,SW 中没有 window.location// 用 event.request.url 或 new URL(event.request.url)
cache.match(),命中则直接返回;未命中才 fetch() 并写入 cache(注意 clone 响应体)app.a1b2c3.js)比依赖版本号更可靠,可彻底规避缓存刷新问题Cache-Control: no-cache,这会让浏览器跳过 SW 的 fetch 事件默认情况下,新 Service Worker 安装后会进入 waiting 状态,直到所有旧页面关闭。用户不刷新就看不到更新——这不是 bug,是设计保障一致性。
self.skipWaiting() 在 install 事件中可跳过 waiting,但必须配合 clients.claim() 在 activate 中主动接管已打开页面skipWaiting 不够:如果页面已加载完 HTML 和 JS,即使 SW 激活了,旧 JS 仍运行,不会自动重 fetch 资源。真正生效要等下次导航或手动刷新message 事件中监听 'SKIP_WAITING',由页面 JS 主动 postMessage 触发,而非无条件跳过最易被忽略的一点:HTML 文件本身通常不应被 SW 缓存(除非你用 PRPL 模式),否则用户永远拿不到含新资源 hash 的最新 HTML,整个缓存链就断了。