Skip to content

2.1 安装、worker 脚本与启动 ​

本章完成三件事:安装依赖 → 生成 worker 脚本 → 在应用入口启动。跟着做完,你的第一个请求就会被拦截。

1. 安装 ​

bash
pnpm add msw
# GraphQL 支持需要额外安装 peer 依赖 graphql
pnpm add graphql

MSW 3 按协议分包导入,安装的包体积由你实际用到的入口决定:

ts
import { http, HttpResponse, passthrough } from "msw";
import { setupWorker } from "msw/browser";   // 仅浏览器
import { setupServer } from "msw/node";      // 仅 Node / Vitest
import { graphql } from "msw/graphql";       // GraphQL
import { ws } from "msw/ws";                 // WebSocket

2. 生成 worker 脚本 ​

MSW 需要把一个官方提供的 Service Worker 文件放到站点根目录,它必须与应用同源:

bash
npx msw init public/ --save
# ✔ Mock Service Worker successfully copied!

生成的 public/mockServiceWorker.js:

  • 要提交到 git(它是运行时资产,别加进 .gitignore);
  • 升级 msw 后重新执行一次该命令保持版本一致;
  • --save 会在 package.json 写入 msw.workerDirectory,让升级脚本能自动找到目录。

Vite / VitePress / Vue CLI / Create React App 的静态目录都叫 public/; Next.js 用 public/,Angular 用 src/。以构建工具文档为准。

3. 写第一个 handler ​

ts
// src/mocks/handlers.ts
import { http, HttpResponse } from "msw";

// 建议用通配源 "*/api/users":浏览器与 Node 测试用同一份 handler 都能匹配
export const handlers = [
  http.get("*/api/users", () => {
    return HttpResponse.json([
      { id: 1, name: "请求小邮差", role: "admin" },
    ]);
  }),

  http.post("*/api/users", async ({ request }) => {
    const body = (await request.json()) as { name?: string };
    if (!body.name) {
      // 统一错误信封:422 + 自定义 code
      return HttpResponse.json(
        { error: { code: "VALIDATION_FAILED", message: "name 必填" } },
        { status: 422 },
      );
    }
    return HttpResponse.json({ id: 2, ...body }, { status: 201 });
  }),
];

为什么路径写 */api/users 而不是 /api/users? MSW 3 的 Node 拦截器不会对相对路径做 origin 补全,相对路径只在浏览器中可用。 通配源(*)在浏览器和 Node 中语义一致,一套 handler 到处跑。

4. 浏览器入口启动 ​

tsx
// src/main.tsx(React)/ src/main.ts(Vue)
import { setupWorker } from "msw/browser";
import { handlers } from "./mocks/handlers";

async function bootstrap() {
  // 生产式写法:开发环境才启用 mock;本教程示例为了演示,所有环境都启用
  if (import.meta.env.DEV) {
    const worker = setupWorker(...handlers);
    await worker.start({
      serviceWorker: { url: "/mockServiceWorker.js" },
      onUnhandledFrame: "warn", // 未命中的请求:bypass(默认) | warn | error
    });
  }

  // 务必 await 之后再渲染,否则首屏请求可能早于拦截
  createRoot(document.getElementById("root")!).render(<App />);
}

void bootstrap();

worker.start() 的常用选项:

选项含义
serviceWorker.urlworker 脚本 URL,默认 /mockServiceWorker.js
onUnhandledFrame未匹配请求/帧的策略:"bypass" 放行、"warn" 控制台警告、"error" 直接报错
quiettrue 时不打印请求日志横幅

5. 验证 ​

启动应用后打开 DevTools:

  • Console 应出现 [MSW] Mocking enabled(worker 注册成功);
  • Application → Service Workers 能看到 mockServiceWorker.js 处于 activated;
  • Network 面板中 /api/users 请求的 Initiator 列指向 worker,响应大小、状态码一切如常。

下面这个控制台就在当前文档页里运行(本站点也启动了同一个 worker)——试着把路径改成 /api/status/404 再发送:

第一个被拦截的请求

6. 可选:msw/vite 零文件集成 ​

MSW 3 还提供 Vite 插件,免生成静态文件:

ts
// vite.config.ts
import { defineConfig } from "vite";
import { msw } from "msw/vite";

export default defineConfig({
  plugins: [msw({ handlers: "./src/mocks/handlers.ts" })],
});

适合快速原型;本教程统一使用显式 worker 文件,因为它更贴近真实集成、便于理解 Service Worker 生命周期(见 3.2 Worker 生命周期与调试)。

下载示例文件 ​