service-worker
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseService Worker
Service Worker
Table of Contents
目录
- Constraints
- Lifecycle
- Registration
- Install Event — Pre-cache Assets
- Activate Event — Clean Up Old Caches
- Fetch Event — Intercept Requests
- Navigation Preload
- Updating a Service Worker
- Communicating with Pages
- Common Pitfalls
- Push Notifications & Background Sync
- API Quick Reference
- Next.js Integration
- DevTools
Constraints
限制条件
- HTTPS required (localhost exempt for dev)
- No DOM access — runs on separate thread
- Fully async — no synchronous XHR, no localStorage
- No dynamic — only static
import()statementsimport - Scope defaults to the directory containing the SW file
- refers to
selfServiceWorkerGlobalScope
- 要求HTTPS(开发环境下localhost除外)
- 无法访问DOM——运行在独立线程中
- 完全异步——不支持同步XHR,不支持localStorage
- 不支持动态——仅支持静态
import()语句import - 作用域默认为包含SW文件的目录
- 指向
selfServiceWorkerGlobalScope
Lifecycle
生命周期
register() → Download → Install → [Wait] → Activate → Fetch control- Register from main thread via
navigator.serviceWorker.register() - Install event fires once — use to pre-cache static assets
- Wait — new SW waits until all tabs using old SW are closed (skip with )
self.skipWaiting() - Activate event fires — use to clean up old caches
- Fetch events start flowing — SW controls page network requests
A document must reload to be controlled (or call during activate).
clients.claim()register() → Download → Install → [Wait] → Activate → Fetch control- 注册:通过从主线程注册
navigator.serviceWorker.register() - Install事件仅触发一次——用于预缓存静态资源
- 等待:新的SW会等待所有使用旧SW的标签页关闭(可通过跳过)
self.skipWaiting() - Activate事件触发——用于清理旧缓存
- Fetch事件开始触发——SW控制页面的网络请求
文档必须重新加载才能被SW控制(或在Activate阶段调用)。
clients.claim()Registration
注册
js
// main.js — register from the page
if ("serviceWorker" in navigator) {
const reg = await navigator.serviceWorker.register("/sw.js", { scope: "/" });
// reg.installing | reg.waiting | reg.active
}Scope rules:
- SW at can control
/sw.jsand all subpaths/ - SW at can only control
/app/sw.jsby default/app/ - Broaden scope with response header
Service-Worker-Allowed
js
// main.js — register from the page
if ("serviceWorker" in navigator) {
const reg = await navigator.serviceWorker.register("/sw.js", { scope: "/" });
// reg.installing | reg.waiting | reg.active
}作用域规则:
- 位于的SW可以控制
/sw.js及所有子路径/ - 位于的SW默认仅能控制
/app/sw.js/app/ - 可通过响应头扩大作用域
Service-Worker-Allowed
Install Event — Pre-cache Assets
Install 事件——预缓存资源
js
// sw.js
const CACHE_NAME = "v1";
const PRECACHE_URLS = ["/", "/index.html", "/style.css", "/app.js"];
self.addEventListener("install", (event) => {
event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS)));
});waitUntil(promise)js
// sw.js
const CACHE_NAME = "v1";
const PRECACHE_URLS = ["/", "/index.html", "/style.css", "/app.js"];
self.addEventListener("install", (event) => {
event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS)));
});waitUntil(promise)Activate Event — Clean Up Old Caches
Activate 事件——清理旧缓存
js
self.addEventListener("activate", (event) => {
event.waitUntil(
caches
.keys()
.then((keys) =>
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))),
),
);
});js
self.addEventListener("activate", (event) => {
event.waitUntil(
caches
.keys()
.then((keys) =>
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))),
),
);
});Fetch Event — Intercept Requests
Fetch 事件——拦截请求
js
self.addEventListener("fetch", (event) => {
event.respondWith(caches.match(event.request).then((cached) => cached || fetch(event.request)));
});respondWith(promise)ResponseFor caching strategy patterns (cache-first, network-first, stale-while-revalidate), see references/caching-strategies.md.
js
self.addEventListener("fetch", (event) => {
event.respondWith(caches.match(event.request).then((cached) => cached || fetch(event.request)));
});respondWith(promise)Response关于缓存策略模式(缓存优先、网络优先、回退到缓存并更新),请查看references/caching-strategies.md。
Navigation Preload
导航预加载
Avoid the startup delay when a SW boots to handle a navigation:
js
self.addEventListener("activate", (event) => {
event.waitUntil(self.registration?.navigationPreload.enable());
});
self.addEventListener("fetch", (event) => {
event.respondWith(
(async () => {
const cached = await caches.match(event.request);
if (cached) return cached;
const preloaded = await event.preloadResponse;
if (preloaded) return preloaded;
return fetch(event.request);
})(),
);
});避免SW启动处理导航时的延迟:
js
self.addEventListener("activate", (event) => {
event.waitUntil(self.registration?.navigationPreload.enable());
});
self.addEventListener("fetch", (event) => {
event.respondWith(
(async () => {
const cached = await caches.match(event.request);
if (cached) return cached;
const preloaded = await event.preloadResponse;
if (preloaded) return preloaded;
return fetch(event.request);
})(),
);
});Updating a Service Worker
更新 Service Worker
- Browser byte-compares the SW file on each navigation (or every 24h)
- New version installs in background while old version still serves
- Increment the cache name (e.g., →
v1) in the new versionv2 - Delete old caches in the handler
activate - Call in
self.skipWaiting()to activate immediatelyinstall - Call in
self.clients.claim()to take control of open pagesactivate
- 浏览器会在每次导航时对比SW文件的字节(或每24小时一次)
- 新版本会在后台安装,同时旧版本仍在提供服务
- 在新版本中递增缓存名称(例如→
v1)v2 - 在处理函数中删除旧缓存
activate - 在阶段调用
install立即激活新版本self.skipWaiting() - 在阶段调用
activate获取对已打开页面的控制权self.clients.claim()
Communicating with Pages
与页面通信
js
// Page → SW
navigator.serviceWorker.controller.postMessage({ type: "SKIP_WAITING" });
// SW → Page (via Clients API)
const clients = await self.clients.matchAll({ type: "window" });
clients.forEach((client) => client.postMessage({ type: "UPDATED" }));
// SW listens
self.addEventListener("message", (event) => {
if (event.data?.type === "SKIP_WAITING") self.skipWaiting();
});js
// 页面 → SW
navigator.serviceWorker.controller.postMessage({ type: "SKIP_WAITING" });
// SW → 页面(通过Clients API)
const clients = await self.clients.matchAll({ type: "window" });
clients.forEach((client) => client.postMessage({ type: "UPDATED" }));
// SW 监听消息
self.addEventListener("message", (event) => {
if (event.data?.type === "SKIP_WAITING") self.skipWaiting();
});Common Pitfalls
常见陷阱
- Response cloning — before both caching and returning, since body streams can only be read once
response.clone() - Opaque responses — cross-origin fetches without CORS return opaque responses (status 0). will refuse them. Use
cache.add()but you can't inspect the responsecache.put() - waitUntil timing — call synchronously within the event handler, not inside an async callback
event.waitUntil() - Scope ceiling — a SW cannot control URLs above its own directory unless header is set
Service-Worker-Allowed - No state persistence — the SW may terminate at any time when idle. Don't store state in global variables — use Cache API or IndexedDB
- 响应克隆——在缓存和返回响应前调用,因为响应体流只能被读取一次
response.clone() - 不透明响应——没有CORS的跨域请求会返回不透明响应(状态码0)。会拒绝这类响应。可使用
cache.add()但无法检查响应内容cache.put() - waitUntil 时机——在事件处理函数内同步调用,而非异步回调中
event.waitUntil() - 作用域上限——SW无法控制其所在目录之上的URL,除非设置了头
Service-Worker-Allowed - 无状态持久化——SW在闲置时可能随时终止。不要在全局变量中存储状态——使用Cache API或IndexedDB
Push Notifications & Background Sync
推送通知与后台同步
For push subscription, handling push events, and background sync implementation, see references/push-and-sync.md.
关于推送订阅、处理推送事件和后台同步的实现,请查看references/push-and-sync.md。
API Quick Reference
API 速查
For detailed interfaces (, , , , , ), see references/api-reference.md.
CacheCacheStorageFetchEventClientsServiceWorkerRegistrationServiceWorkerGlobalScope关于详细的接口(、、、、、),请查看references/api-reference.md。
CacheCacheStorageFetchEventClientsServiceWorkerRegistrationServiceWorkerGlobalScopeNext.js Integration
Next.js 集成
In Next.js, place the service worker file in . is intentionally plain JS (not processed by Next.js build pipeline). Register it from a client component:
public/sw.jspublic/sw.jstsx
"use client";
import { useEffect } from "react";
export function ServiceWorkerRegistrar() {
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
}, []);
return null;
}Add to root layout. Next.js serves files at the root, so scope covers .
public//sw.js/在Next.js中,将service worker文件放在目录下。是纯JS文件(不会被Next.js构建流程处理)。从客户端组件中注册它:
public/sw.jspublic/sw.jstsx
"use client";
import { useEffect } from "react";
export function ServiceWorkerRegistrar() {
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
}, []);
return null;
}将该组件添加到根布局中。Next.js会将目录下的文件部署到根路径,因此的作用域覆盖。
public//sw.js/DevTools
开发者工具
- Chrome: or Application > Service Workers
chrome://inspect/#service-workers - Firefox: or Application > Service Workers
about:debugging#/runtime/this-firefox - Edge: or Application > Service Workers
edge://inspect/#service-workers
Unregister, update, and inspect caches from the Application panel. Use "Update on reload" checkbox during development.
- Chrome:或 开发者工具 > Application > Service Workers
chrome://inspect/#service-workers - Firefox:或 开发者工具 > Application > Service Workers
about:debugging#/runtime/this-firefox - Edge:或 开发者工具 > Application > Service Workers
edge://inspect/#service-workers
可从Application面板中注销、更新SW并检查缓存。开发期间可勾选"Update on reload"复选框。