# Service Worker

> PWA 离线能力背后的可编程网络代理：一段最小注册代码、一次讲完生命周期、常见陷阱，以及各部分的详解在哪。

import { CardGrid, LinkCard } from '@astrojs/starlight/components';

**Service Worker** 是浏览器在独立 worker 线程中运行的脚本，与任何页面分离。它可以拦截
它所控制页面发出的网络请求并自行响应，这正是离线支持、二次访问秒开与推送得以成立的原因：
标签页关闭之后，这个 worker 仍在运行。

它没有 DOM 访问权限，且只在安全上下文中运行（HTTPS，开发时可用 `localhost`）。

## 最小可用的那一个

在页面中注册：

```js
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
} else {
  // 没有 service worker：站点照常工作，只是没有离线层。
}
```

在 `/sw.js` 中用缓存响应请求，回退到网络：

```js
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((hit) => hit || fetch(event.request)),
  );
});
```

这就是一个完整可用的 service worker。其余的一切（预缓存、更新策略、过期）
都是在这两块之上的精修。

## 一次讲完生命周期

注册后的 worker 先 **install**，再 **activate**，之后才开始控制页面。在 worker 激活之前
就已加载的页面，要到重新加载后才会被控制。当你发布新的 `sw.js`，浏览器会在旧 worker 之外
安装它，并让它处于 **waiting** 状态，直到旧 worker 释放其客户端，这就是为什么一次发布
不会在下一次刷新时立刻生效，除非你显式要求。

见 [Service worker 生命周期](/zh/reference/service-worker/lifecycle/) 与
[更新流程与 skipWaiting](/zh/reference/service-worker/update-skipwaiting/)。

## 常见的出错点

- **作用域**：位于 `/js/sw.js` 的 worker 只控制 `/js/`。把它放在根目录，或发送
  `Service-Worker-Allowed`。见[注册与作用域](/zh/reference/service-worker/registration-scope/)。
- **发布后用户仍看到旧版**：新 worker 卡在旧 worker 后面等待。
- **不加区分地缓存一切**：对 HTML 用 cache-first 会把用户困在旧页面上。按请求类型选择：
  [缓存策略](/zh/reference/service-worker/caching-strategies/)。
- **当成页面代码去调试**：它有自己的生命周期和自己的 DevTools 面板。
  见[调试 service worker](/zh/reference/service-worker/debugging/)。

## 本节全部主题

<CardGrid>
<LinkCard title="Service worker 生命周期" href="/zh/reference/service-worker/lifecycle/" description="service worker 从 register() 到 redundant 经过哪些状态、每次转换触发什么事件、新版本为何等待，以及 skipWaiting() 改变什么。" />
<LinkCard title="Service worker 注册与 scope" href="/zh/reference/service-worker/registration-scope/" description="register() 如何把脚本绑定到 URL 前缀 scope，默认 scope 与 Service-Worker-Allowed 如何限定它，重叠如何裁决，以及 updateViaCache。" />
<LinkCard title="skipWaiting() 与更新流程" href="/zh/reference/service-worker/update-skipwaiting/" description="浏览器如何检测字节不同的 worker 脚本、新 worker 为何停在 waiting，以及 skipWaiting()、clients.claim()、update() 与 updateViaCache 各改变什么。" />
<LinkCard title="FetchEvent 与请求路由" href="/zh/reference/service-worker/fetch-event/" description="service worker 收到的 FetchEvent 成员，respondWith() 与 waitUntil() 的规则及错误结果，以及按 destination、mode 与 URL 路由的写法。" />
<LinkCard title="Service worker 缓存策略" href="/zh/reference/service-worker/caching-strategies/" description="Cache First、Network First、Stale-While-Revalidate、Network Only、Cache Only 各返回什么、代价是什么，两个陷阱，以及对应的 Workbox 类。" />
<LinkCard title="Cache API" href="/zh/reference/service-worker/cache-api/" description="caches 背后的 CacheStorage 与 Cache 接口及其成员，add()、addAll()、put() 抛错的条件，以及 install 与 activate 中的版本化缓存模式。" />
	<LinkCard title="Clients API" href="/zh/reference/service-worker/clients-api/" description="self.clients 暴露的 Clients 接口及其 get()、matchAll()、openWindow()、claim() 成员，抛出 InvalidAccessError 的条件，以及通知点击聚焦窗口的模式。" />
	<LinkCard title="NavigationPreloadManager" href="/zh/reference/service-worker/navigation-preload/" description="registration.navigationPreload 如何让导航请求与 service worker 启动并行，它的四个成员与异常条件，以及 preloadResponse 与请求头如何配合。" />
<LinkCard title="Service worker 离线兜底" href="/zh/reference/service-worker/offline-fallback/" description="请求失败且 URL 无缓存时，service worker 如何返回预缓存的页面、图片或 JSON，Response.error() 做什么，以及 Workbox 的等价写法。" />
	<LinkCard title="Background Fetch API" href="/zh/reference/service-worker/background-fetch/" description="BackgroundFetchManager.fetch() 如何把大文件下载交给浏览器显示进度，完成后以哪些事件唤醒 service worker，以及它的异常与失败原因。" />
	<LinkCard title="Periodic Background Sync API" href="/zh/reference/service-worker/periodic-background-sync/" description="PeriodicSyncManager.register() 如何按 minInterval 在已安装 PWA 中触发 periodicsync，Chrome 的安装与参与度门槛，以及异常与检测。" />
<LinkCard title="Workbox" href="/zh/reference/service-worker/workbox/" description="Workbox 7 各包做什么，precacheAndRoute() 与 __WB_MANIFEST 如何生成带版本的缓存，GenerateSW 与 InjectManifest 何时适用，以及页面更新。" />
<LinkCard title="Service worker 调试" href="/zh/reference/service-worker/debugging/" description="Chrome DevTools、Firefox DevTools 与 Safari Web Inspector 在哪里暴露 service worker 状态，如何强制更新、绕过 worker、模拟离线，附面板原文。" />
	<LinkCard title="Background Sync API" href="/zh/reference/service-worker/background-sync/" description="SyncManager.register() 如何把失败请求推迟到恢复连接后在 sync 事件中重放，以及它的异常、重试上限与检测方式。" />
</CardGrid>

## 延伸阅读

- [Web 应用清单](/zh/reference/manifest/)：可安装应用的另一半。
- [安装提示](/zh/reference/installation/install-prompt/)：浏览器在提供安装前要求什么。

← 返回[参考](/zh/reference/)总览。