# Service Worker 启动与 fetch 开销

> 说明已停止的 Service Worker 如何延迟 fetch 处理、如何测量导航时序，以及何时用预加载或移除空处理器来避免开销。

已停止的 Service Worker 必须先启动，才能运行 `fetch` 处理器。如果处理器随后访问网络，启动和处理器执行会发生在网络请求之前；导航预加载可以让导航请求与 Worker 启动并行（[Service worker deployment](https://developer.chrome.com/docs/workbox/service-worker-deployment)，Chrome for Developers）。

BCD 记录 `NavigationPreloadManager` 从 Chrome 59、Edge 18、Firefox 99 和 Safari 15.4 起得到支持；该 API 可缓解这里所述的启动开销（[NavigationPreloadManager.json](https://raw.githubusercontent.com/mdn/browser-compat-data/main/api/NavigationPreloadManager.json)，MDN browser-compat-data）。

## 工作原理

浏览器会在 Service Worker 进程不活跃时停止它，并在需要分派事件时启动它。因此，进入 `fetch` 处理器的受控导航要等待已停止的 Worker 启动。缓存响应可以抵消这项成本；调用 `fetch(event.request)` 的透传处理器仍需执行网络工作，并在此之前增加 Worker 路径（[Service worker deployment](https://developer.chrome.com/docs/workbox/service-worker-deployment)，Chrome for Developers）。

Navigation Timing 暴露 `workerStart`，表示请求开始由 Worker 处理的时点。页面不受 Service Worker 控制时，该值为 `0`。Chrome DevTools 在 Network 面板的 Timing 标签中把对应的请求阶段标为 `ServiceWorker Preparation` 和 `Request to ServiceWorker`（[Network features reference](https://developer.chrome.com/docs/devtools/network/reference)，Chrome for Developers）。

导航预加载作用于导航请求。在注册上启用后，浏览器可以在 Worker 启动时开始网络请求，处理器通过 `event.preloadResponse` 读取该响应。它不会移除其他处理器逻辑的执行成本；处理器从缓存响应时，它还可能浪费一次请求。

:::caution
Chrome 115 引入一项优化。如果 Chromium 识别出所有 `fetch` 监听器都是空操作，就会省略 Worker 启动和事件分派。调用 `fetch(event.request)` 的透传监听器不是空操作，仍会使用上文所述的 Worker 路径。对于没有响应逻辑的监听器，应将它移除，不要依赖这项 Chromium 优化（[Skip service worker no-op fetch handler](https://chromestatus.com/feature/5136946693668864)，Chrome Status）。
:::

:::observed
从 Chrome 112 起，Chromium 会输出确切的控制台警告：`Fetch event handler is recognized as no-op. No-op fetch handler may bring overhead during navigation. Consider removing the handler if possible.`。其 DevTools 测试和消息定义记录在所引用的 [Chromium change](https://chromium.googlesource.com/chromium/src/+/b21c192cac10afc37b36d92446a6bdc441f35893) 中（chromium.googlesource.com），版本由 [Chrome Status](https://chromestatus.com/feature/5136946693668864) 记录。
:::

## 示例

先在页面中测量启动，再只对依赖网络的导航使用预加载。

### 测量受控导航

回退分支报告这次导航没有 Service Worker 时序，而不是拿值为零的哨兵做减法。

```js
const [navigation] = performance.getEntriesByType('navigation');

if (!navigation || navigation.workerStart === 0) {
  console.log('This navigation has no service worker timing.');
} else {
  const beforeWorker = navigation.workerStart - navigation.startTime;
  const workerToRequest = navigation.requestStart - navigation.workerStart;
  console.table({ beforeWorker, workerToRequest });
}
```

应在真实设备上收集这些字段。部署指南指出，启动时间随设备能力和 CPU 压力变化，因此单台开发机器上的值不能作为通用预算。

### 让启动与导航请求重叠

激活期间检测注册是否暴露预加载，再启用它。fetch 处理器使用预加载响应；预加载不存在或没有产生响应时，回退到普通 fetch。

```js
self.addEventListener('activate', (event) => {
  if (!('navigationPreload' in self.registration)) return;
  event.waitUntil(self.registration.navigationPreload.enable());
});

self.addEventListener('fetch', (event) => {
  if (event.request.mode !== 'navigate') return;

  event.respondWith((async () => {
    const preloaded = await event.preloadResponse;
    if (preloaded) return preloaded;
    return fetch(event.request);
  })());
});
```

如果导航通常来自 Cache Storage，应先比较省下的网络时间与未使用的预加载请求，再决定是否启用。

## 更正（2026-10-23）

Chrome 112 引入了空操作 `fetch` 处理器的控制台警告；省略 Worker 启动与事件分派的优化在 Chrome 115 发布。BCD 支持说明现已限定为引用数据文件所记录的 `NavigationPreloadManager`（[Skip service worker no-op fetch handler](https://chromestatus.com/feature/5136946693668864)，Chrome Status；[NavigationPreloadManager.json](https://raw.githubusercontent.com/mdn/browser-compat-data/main/api/NavigationPreloadManager.json)，MDN browser-compat-data）。

## 另请参阅

- [导航预加载与 Service Worker 启动时间](/zh/reference/performance/navigation-preload/)
- [Service Worker 静态路由 API](/zh/reference/service-worker/static-routing/)
- [Service Worker 缓存策略](/zh/reference/service-worker/caching-strategies/)
- [Speed up service worker with navigation preloads](https://web.dev/blog/navigation-preload)（web.dev）
- [Network features reference](https://developer.chrome.com/docs/devtools/network/reference)（Chrome for Developers）