Service Worker · 概念
Service worker 调试
发布于
调试 service worker 就是去读页面自己显示不出来的三样东西:注册的状态(installing、waiting、active)、Cache Storage 的内容,以及哪些请求是由 worker 应答的。Chrome 40+ 与 Edge 79+ 在 DevTools › Application 中集中展示这三项;Firefox 44+ 把它们分散在 about:debugging 与 Storage 面板;Safari 11.1+ 在 Web Inspector 中列出注册,但没有缓存查看器,也没有强制更新控件(BCD api.ServiceWorker;厂商文档见来源)。
各家面板不同,但每个面板都只是同一个注册对象、同一个 worker 所见 caches 的一种视图,因此面板能做的事都可以在 worker 的控制台上下文里完成。
Chrome 与 Edge DevTools
Section titled “Chrome 与 Edge DevTools”Application › Service workers 列出该源的每条注册,带 scope、脚本 URL 与一行状态,格式为 #<id> activated and is running、#<id> waiting to activate 或 #<id> trying to install。顶部三个复选框只改变当前标签页的行为:Offline 让 worker 内与页面中的 fetch() 像断网一样失败;Update on reload 在每次导航时强制逐字节比较并把抓取到的脚本作为新 worker 安装(绕过 24 小时脚本缓存规则);Bypass for network 把所有请求直接送往网络,不在 worker 中触发 fetch。每条注册有 Update、Unregister 链接,处于等待状态的 worker 旁还有 skipWaiting 链接,无需改代码即可对它调用 skipWaiting()。Application › Cache storage 以 <缓存名> - <源> 列出缓存,显示每个条目的请求 URL、响应头与正文预览,并支持按条目或按整个缓存删除。Network 面板中,由 worker 产生的响应在 Size 列显示 (ServiceWorker),worker 自己发出的请求带齿轮图标。Console 面板左上角的上下文选择器(默认 top)以脚本 URL 列出 worker;选中后,worker 内的 console.log 会显示在这里,也可以在 worker 的全局作用域中执行 await caches.keys() 或 await self.clients.matchAll()。
Firefox DevTools
Section titled “Firefox DevTools”about:debugging#/runtime/this-firefox 在 Service Workers 下列出注册,带 scope、状态与两个按钮:Inspect 打开一个以 worker 为上下文的工具箱(其 Console 就是 worker 控制台),Unregister 注销。页面的工具箱也在 Application › Service Workers 中显示该 worker(Firefox 79+),已停止的 worker 旁有 Start 按钮。Firefox 没有“重新加载时更新”开关;等价做法是在页面控制台调用 registration.update(),或先 Unregister 再刷新。Cache Storage 出现在 Storage 面板的 Cache Storage 下,列出各缓存及其请求 URL,并可删除条目。
Safari Web Inspector
Section titled “Safari Web Inspector”启用 Develop 菜单(Safari › 设置 › 高级 › Show features for web developers,Safari 17 之前的标签为 Show Develop menu in menu bar),再打开 Develop › Service Workers,它按 scope 列出注册,并打开一个附着到 worker 上下文的 Web Inspector 窗口。Storage 标签显示 IndexedDB 与 Local Storage,但不显示 Cache Storage,因此要列出缓存条目只能在 worker 控制台执行 await caches.keys() 与 await (await caches.open(name)).keys()。没有离线模拟与强制更新;改用 Network Link Conditioner(macOS)或飞行模式(iOS),以及 registration.update()。
下面每种故障都在某个面板中有可见的特征。
| 症状 | 显示位置 | 原因与修法 |
|---|---|---|
| 新代码没有运行 | Chrome:第二个 worker 旁显示 waiting to activate |
旧标签页仍被控制;关闭它们、点 skipWaiting,或在代码中提供 skipWaiting() 路径 |
| 部署被忽略最多一天 | Network 面板显示脚本来自 (disk cache) |
脚本被 HTTP 缓存;注册时传 updateViaCache: 'none',或给脚本 URL 加 Cache-Control: no-cache |
| 请求没被拦截 | Network 的 Size 列没有 (ServiceWorker) |
页面在 scope 之外、worker 尚未激活,或开着 Bypass for network |
| 安装后缓存为空 | Cache storage 没有缓存,worker 控制台显示 addAll() 被拒绝 |
某个预缓存 URL 返回了非 2xx 状态;addAll() 是原子的,任一失败则什么都不存 |
| 安装卡住 | 状态停在 trying to install |
event.waitUntil() 收到了一个永不落定的 Promise |
下面两段代码用脚本重现最常用的两个面板操作,因此在没有对应按钮的浏览器中同样可用。
在页面控制台强制更新检查并列出缓存内容
Section titled “在页面控制台强制更新检查并列出缓存内容”registration.update() 执行的正是 “Update on reload” 触发的那次逐字节比较;页面中的 caches.keys() 列出的也正是 Application 面板显示的那些缓存,因为同一源上的页面与 worker 共享 caches。
const registration = await navigator.serviceWorker.getRegistration();await registration.update(); // 抓取 /sw.js,有差异则安装console.log(registration.waiting?.state); // 有更新在等待时为 "installed"
for (const name of await caches.keys()) { const cache = await caches.open(name); const keys = await cache.keys(); console.log(name, keys.map((request) => request.url));}页面侧的列举在没有缓存查看器的 Safari 中同样有效;代价是 opaque 响应只能看到 URL,它们的正文在任何上下文中都读不出来。
用一个调试开关记录每次 fetch 决策,生产环境下为空操作
Section titled “用一个调试开关记录每次 fetch 决策,生产环境下为空操作”在 fetch 处理器里打日志,是 Workbox 开发构建日志的手工版本。以注册 URL 为开关可以让生产 worker 保持安静且无需构建步骤;console 不可用时(较老的 WebView)守卫直接什么都不做。
const DEBUG = new URL(self.location.href).searchParams.has('debug'); // register('/sw.js?debug')
self.addEventListener('fetch', (event) => { event.respondWith((async () => { const cached = await caches.match(event.request); if (DEBUG && typeof console !== 'undefined') { console.log(cached ? 'cache' : 'network', event.request.method, event.request.url); } return cached ?? fetch(event.request); })());});注册 /sw.js?debug 安装的是另一个脚本 URL,因而是另一个 worker;测量前记得改回不带查询串的注册,否则带日志的 worker 会一直控制该标签页。
- Debug Progressive Web Apps(developer.chrome.com)
- about:debugging(firefox-source-docs.mozilla.org)
- Workbox: troubleshooting and logging(developer.chrome.com)
- skipWaiting() 与更新流程
- Service worker 生命周期
- Cache API
- Service worker 注册与 scope
规范
| 规范 | 状态 |
|---|---|
| 无。 | |