3.2 Service Worker 生命周期与调试
理解 worker 的生命周期,才能解释"为什么我改了 handler 没生效""为什么首次刷新不拦截"这类问题。
注册到激活
worker.start() 内部经历标准 Service Worker 生命周期:
register(mockServiceWorker.js)
│
▼
installing ── 下载并解析脚本,版本由文件内容决定
│
▼
installed(等待) ── 旧 worker 仍控制页面时,新 worker 在队列等待
│ (skipWaiting/clients.claim 后立即接管,MSW 已处理)
▼
activating → activated ── 开始拦截 fetch 事件
│
▼
fetch / message 事件循环(你的 handlers 在这里执行)
│
▼
redundant ── 被新脚本取代或注销MSW 的 worker 脚本内置了 skipWaiting + clients.claim,因此注册后立刻生效,不用手动关标签页。
为什么必须 await start()
ts
// ❌ 首屏请求可能在 worker activated 之前发出,直接穿透到网络(404)
worker.start();
fetch("/api/me");
// ✅ start() resolve 时 worker 已激活
await worker.start();
fetch("/api/me");本教程的 React/Vue 示例都在入口 await startMockWorker() 之后才挂载根组件。
handler 顺序 = 匹配优先级
ts
setupWorker(
http.get("*/api/users/:id", getUser), // 先注册,先匹配
http.get("*/api/*", fallback), // 兜底放最后
);- resolver 返回了
Response:匹配结束; - resolver 什么都没返回(
undefined):继续向下找下一个匹配的 handler(叫 passthrough-by-omission); - 显式调用
passthrough():直接放行到真实网络:
ts
import { http, passthrough } from "msw";
http.get("*/assets/*", () => passthrough()), // 静态资源不 mock
http.get(/.*/, () => passthrough()), // 其余全部放行运行时增删 handler
ts
// 测试或调试场景非常有用:临时加一个 handler
worker.use(
http.get("*/api/users", () => HttpResponse.json([{ id: 999 }])),
);
// 恢复到 setupWorker 时的初始集合
worker.resetHandlers();
// 移除通过 use() 加入的(restoreHandlers 与 resetHandlers 的区别见官方 API)
worker.restoreHandlers();onUnhandledFrame:未命中策略
MSW 3 用 onUnhandledFrame 统一了 HTTP 与 WebSocket:
ts
worker.start({ onUnhandledFrame: "error" });| 取值 | 行为 | 适用 |
|---|---|---|
"bypass"(默认) | 静默放行到真实网络 | 开发期,页面有真实静态资源/埋点 |
"warn" | Console 警告但放行 | 排查"为什么没匹配上" |
"error" | 抛错 | 测试里强制"所有请求必须被声明" |
也可以传函数做精细化控制(比如只对 /api 报错、埋点请求放行)。
调试三板斧
- Console 横幅:
[MSW] 0.xx.x mocking enabled.(quiet 关闭时)与每条命中日志。 - Network 面板:请求真实存在,响应头里可以看到 MSW 标记;看
Initiator能确认是否走 worker。 - Application → Service Workers:手动 Update / Unregister / Offline 模拟。
常见问题:
| 现象 | 原因与处理 |
|---|---|
[MSW] Failed to register | worker 路径错或同源策略问题;确认脚本能通过 /mockServiceWorker.js 访问 |
| handler 改了不生效 | 热更新偶发;DevTools → Service Workers → Update,或硬刷新 |
| Node 下相对路径不匹配 | Node 不做 origin 补全,handler 路径用 */api/... 通配源 |
| 首次加载有一个请求漏网 | 没 await worker.start() 就发请求 |
| worker 版本升级后行为怪 | 重新 npx msw init public/,并 Unregister 旧 worker |
Node 端没有 Service Worker
setupServer 在 Node 中由 @mswjs/interceptors 直接劫持 fetch / http / xmlhttprequest(兼容层),没有生命周期一说:
ts
server.listen({ onUnhandledFrame: "error" }); // beforeAll
server.resetHandlers(); // afterEach,防止测试互相污染
server.close(); // afterAll详细测试实践见 6.2 Node 环境与单元测试。