# Service worker 调试

> 如何使用 Chrome DevTools、Firefox DevTools 和 Safari Web Inspector 检查、强制更新并诊断 service worker——涵盖注册状态、缓存内容、fetch 拦截与更新流程。

**一句话：** Chrome DevTools 的应用面板（以及 Firefox 和 Safari 的等效面板）让你无需部署任何改动，即可检查注册状态、强制更新、测试离线、清空缓存，并观察 `fetch` 事件的拦截情况。

## Chrome DevTools

### 应用 → Service Workers

主要的调试界面。打开 DevTools → 应用 → Service Workers（左侧边栏）。

| 控件 | 作用 |
|---|---|
| **重新加载时更新** | 每次导航都强制进行字节比较并安装新 worker |
| **绕过网络** | 为此标签页禁用 service worker——所有 `fetch` 事件直接透传 |
| **离线** | 模拟无网络状态（针对经过 service worker 的请求） |
| **更新** | 手动触发 `registration.update()` |
| **取消注册** | 彻底移除注册 |

面板还显示当前状态：*正在安装*、*等待中*或*已激活*——以及脚本 URL 和 scope。

### 强制更新 worker

开发阶段，"重新加载时更新"复选框是测试 worker 改动最快捷的方式。若不勾选，Chrome 最长会在 24 小时内使用缓存的 worker 脚本，即使文件已更改，浏览器也只在检测到字节差异时才安装新 worker。

若新 worker 卡在 *waiting* 状态，面板会显示"skipWaiting"链接，点击即可立即对待激活的 worker 调用 `skipWaiting()`——无需修改 worker 代码即可测试。

### Cache Storage

DevTools → 应用 → Cache Storage 列出所有具名缓存及其条目。你可以查看请求/响应头、预览响应体，以及删除单个条目或整个缓存。

### 检查 fetch 事件

打开 Network 面板，启用 **Service Worker** 列（右键单击列标题）。每个请求都会显示是否由 service worker 提供，以及来自缓存还是网络。"大小"列中的 `(ServiceWorker)` 标签确认 worker 拦截了该请求。

### 在 worker 上下文中使用控制台

将 DevTools 控制台的 JavaScript 上下文（过滤栏旁的下拉菜单）从"top"切换为 service worker URL。worker 内部的 `console.log` 调用会出现在这个上下文中。

你还可以在 worker 的全局作用域中运行表达式，包括检查 `self.registration`、`caches.keys()` 和 `self.clients.matchAll()`。

## Firefox DevTools

打开 **about:debugging#/runtime/this-firefox** → "此 Firefox" → "Service Workers"。Firefox 列出每个已注册的 worker，显示其 scope、状态和 **检查** 链接，点击后会打开该 worker 的专用控制台。

Firefox 没有内置的"重新加载时更新"复选框。开发时若要强制重新安装，可取消注册 worker 后刷新，或在页面控制台中调用 `registration.update()`。

## Safari Web Inspector

在 Safari（iOS 或 macOS）中，开启开发菜单（偏好设置 → 高级 → 在菜单栏中显示"开发"菜单）。打开 Web Inspector → 存储 → Service Workers。Safari 列出已注册的 worker、其 scope 和脚本 URL。

Safari Web Inspector 的调试能力比 Chrome 或 Firefox 有限：没有专用的缓存查看器或"强制更新"复选框。可在 worker 上下文的控制台中使用 `caches.keys()` 进行检查。

## 常见问题诊断

| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| Worker 卡在 *waiting* | 旧标签页未关闭且未调用 `skipWaiting()` | 关闭所有标签页，或使用 DevTools 的"skipWaiting"链接 |
| 部署后新代码未运行 | HTTP 缓存提供了旧 worker 脚本 | 使用 `updateViaCache: 'none'` 或为 worker URL 设置 `Cache-Control: no-cache` |
| 请求未被拦截 | Worker 尚未激活（仍在安装中）或 scope 不匹配 | 在 DevTools 中检查 scope；激活后重新加载 |
| 缓存未填充 | `install` 失败或未调用 `event.waitUntil()` | 在 worker 上下文控制台中检查是否有错误 |
| 提供了过期内容 | `CacheFirst` 策略没有设置过期 | 添加 `ExpirationPlugin` 或为缓存名称加版本号 |
| 未显示离线兜底页面 | 兜底页面未在 `install` 中预缓存，或 `fetch` 事件判断条件有误 | 检查 Cache Storage 中是否有兜底 URL；添加日志 |

## Workbox 日志

Workbox 在开发构建中会输出详细日志。调试时切换到 Workbox 的开发版本：

```js
import { setCacheNameDetails } from 'workbox-core';
// 开发版本会在控制台打印策略决策日志
```

当 `process.env.NODE_ENV !== 'production'` 时，Workbox 会自动选择开发构建。

## 实践清单

- [ ] 开发期间在 DevTools 中启用"重新加载时更新"，避免 worker 缓存造成混乱。
- [ ] 切换到 worker 控制台上下文，查看 worker 端的 `console.log` 输出。
- [ ] 需要临时跳过 service worker 测试时，使用"绕过网络"。
- [ ] 部署后确认 Cache Storage 中只有新版本缓存，旧缓存已被删除。
- [ ] 在 DevTools Network 中勾选"离线"并导航到未缓存的 URL 来测试离线兜底。

## 相关参考

- [更新流程与 skipWaiting](/zh/reference/service-worker/update-skipwaiting/) — 理解 worker 为何卡在 waiting 状态
- [缓存策略](/zh/reference/service-worker/caching-strategies/) — Cache Storage 中可见的各种策略
- [注册与 scope](/zh/reference/service-worker/registration-scope/) — Service Workers 面板中可见的 scope 问题