# Workbox

> Workbox 是什么、其模块化包如何对应 service worker 任务，以及如何在有无构建工具的情况下将其添加到项目中。

**一句话：** Workbox 是 Google 出品的生产级 service worker 库。它将手写的 `fetch` 事件逻辑替换为可组合的模块，涵盖路由、缓存策略、预缓存、后台同步和过期管理——同时保持与原生 Service Workers API 的亲近性。

## Workbox 提供了什么

| 模块 | 用途 |
|---|---|
| `workbox-routing` | 按 URL 模式或请求属性将 `fetch` 事件路由到不同处理器 |
| `workbox-strategies` | `CacheFirst`、`NetworkFirst`、`StaleWhileRevalidate`、`NetworkOnly`、`CacheOnly` 的实现 |
| `workbox-precaching` | 基于清单的预缓存，带完整性校验和缓存清理 |
| `workbox-expiration` | 对任意缓存限制条目数量和 TTL |
| `workbox-cacheable-response` | 按状态码或响应头过滤可缓存的响应 |
| `workbox-background-sync` | 将失败的 POST 请求加入队列，恢复网络后重放 |
| `workbox-broadcast-update` | 缓存响应更新时通知页面 |
| `workbox-google-analytics` | 缓存离线分析事件，恢复连接后重放 |

## 安装

### CDN（无需构建工具）

```js
// sw.js
import { registerRoute } from 'https://cdn.jsdelivr.net/npm/workbox-routing@7/registerRoute.mjs';
import { CacheFirst } from 'https://cdn.jsdelivr.net/npm/workbox-strategies@7/CacheFirst.mjs';

registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({ cacheName: 'images' })
);
```

### npm（配合打包工具）

```sh
npm install workbox-routing workbox-strategies workbox-precaching
```

```js
// sw.js（打包版本）
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst } from 'workbox-strategies';
```

### `workbox-window`（在页面中使用）

```sh
npm install workbox-window
```

```js
import { Workbox } from 'workbox-window';

const wb = new Workbox('/sw.js');
wb.register();

// 监听更新并通知用户
wb.addEventListener('waiting', () => {
  if (confirm('有新版本，是否刷新？')) {
    wb.messageSkipWaiting();
    window.location.reload();
  }
});
```

`workbox-window` 处理注册、更新检测和 `skipWaiting` 消息传递——这些原本需要在每个页面手写的样板代码。

## 使用 `workbox-precaching` 进行预缓存

预缓存使用构建时生成的清单来缓存一组带版本号的 URL。Workbox 追踪清单哈希，只重新获取内容发生变化的条目：

```js
import { precacheAndRoute } from 'workbox-precaching';

// __WB_MANIFEST 由 Workbox 构建工具注入（injectManifest / generateSW）
precacheAndRoute(self.__WB_MANIFEST);
```

生成此清单的构建工具：`workbox-webpack-plugin`（通过 `InjectManifest` 或 `GenerateSW`）、`workbox-vite-plugin`、`workbox-cli` 以及 `@ducanh2912/next-pwa`。

## 路由

```js
import { registerRoute, NavigationRoute } from 'workbox-routing';
import { NetworkFirst, CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
import { ExpirationPlugin } from 'workbox-expiration';

// 页面（SPA 和 MPA）
registerRoute(
  new NavigationRoute(new NetworkFirst({ cacheName: 'pages' }))
);

// 带过期策略的图片
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images',
    plugins: [
      new CacheableResponsePlugin({ statuses: [200] }),
      new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 }),
    ],
  })
);

// 字体——旧版立即响应，长期有效
registerRoute(
  ({ request }) => request.destination === 'font',
  new StaleWhileRevalidate({ cacheName: 'fonts' })
);
```

## `GenerateSW` vs `InjectManifest`

| 模式 | Workbox 的行为 | 适用场景 |
|---|---|---|
| `GenerateSW` | 根据你的配置生成完整的 `sw.js` | 简单应用；worker 中无需自定义逻辑 |
| `InjectManifest` | 将预缓存清单注入你自己的 `sw.js` 模板 | 需要自定义路由、后台同步或预缓存以外的任何代码 |

## Workbox 不覆盖的内容

- Workbox 不负责生成清单或处理代码分割——那是打包工具的职责。
- 它不提供推送通知 API——请直接使用原生 Web Push API。
- Workbox 的后台同步依赖 `BackgroundSyncPlugin`，而该功能依赖 Background Sync API（截至本文写作时，Safari 尚不支持）。

## 实践清单

- [ ] 将 Workbox 固定到特定主版本（`workbox-routing@7`）——生产环境避免使用浮动的 `@latest`。
- [ ] 若需要在 worker 中自定义路由或后台同步逻辑，使用 `InjectManifest`。
- [ ] 在页面中使用 `workbox-window` 实现整洁的注册、更新提示和 `skipWaiting` 协调。
- [ ] 为每条 `CacheFirst` 路由添加 `ExpirationPlugin`，控制缓存体积增长。
- [ ] 通过 DevTools → 应用 → Service Workers → "重新加载时更新"测试预缓存修订逻辑。

## 相关参考

- [缓存策略](/zh/reference/service-worker/caching-strategies/) — Workbox 实现的五种策略
- [Fetch 事件与路由](/zh/reference/service-worker/fetch-event/) — Workbox 所抽象的原生事件
- [更新流程与 skipWaiting](/zh/reference/service-worker/update-skipwaiting/) — `workbox-window` 的 `messageSkipWaiting()` 与此相关