前后端分离之后,联调这件事一度变得很难受:后端接口还没好,前端干瞪眼;后端好了但数据脏,前端凑不出想要的场景;测试想覆盖接口异常分支,得等后端专门造数据。于是大家各显神通——有人在代码里 mock fetch,有人改 hosts 指向假数据,有人等联调环境排期。这些方案的共同毛病是「侵入」:应用代码要知道自己在被 mock,测试代码和生产代码走的路不一样。Mock Service Worker(MSW)给出了另一种思路:在网络层拦截请求,应用完全感知不到 mock 的存在,一套 mock 定义同时服务于浏览器开发、单元测试、集成测试和 E2E。它把自己称为「JavaScript 领域 API mock 的行业标准」,这句话基本没有争议。
项目采用 MIT 协议,TypeScript 编写,当前版本 v3.0.3,最近更新 2026 年 10 月 17 日,GitHub 星数超过 1.8 万。从个人项目到大型企业,它已经是前端工程链路的常见组件。
项目基本信息
| 信息项 | 详情 |
|---|---|
| 项目名称 | Mock Service Worker |
| GitHub地址 | https://github.com/mswjs/msw |
| 项目描述 | 在网络层拦截请求的API mock库,一份定义通吃开发与测试 |
| 作者 | mswjs |
| 开源协议 | MIT License |
| 开源状态 | 公开状态 |
| Languages | TypeScript |
| 支持平台 | Windows / macOS / Linux |
| 最后更新 | 2026-10-17 |
一、项目介绍
MSW 的核心机制是请求拦截。在浏览器里,它注册一个 Service Worker,在请求离开应用之后、到达网络之前把它截下来,按你定义的处理器返回 mock 响应——DevTools 里看到的是一次「真实」的网络请求,只是响应来自本地定义。在 Node 环境(测试)里,它用等效的拦截机制做同样的事。应用代码照常用 fetch、axios、react-query,完全不需要为 mock 做任何改造。
处理器用 Express 风格的路由语法书写,支持路径参数、通配符和正则表达式匹配,可以返回指定状态码、响应头、Cookie、延迟,甚至完全自定义的 resolver。除了 REST,它还覆盖 GraphQL,官方文档有完整指南。一份 handlers 定义,浏览器开发时用 setupWorker 启动,测试里用 setupServer 启动,两边共用同一套网络描述——这是它区别于「在代码里塞假数据」方案的本质差异。
二、核心优势
第一,零侵入。拦截发生在网络层,应用和测试代码对「是否被 mock」无感知,不存在「上线时忘记摘 mock」这类事故。
第二,一份定义多处复用。浏览器本地开发、单元测试、集成测试、E2E 共用同一套 handlers,维护一处,处处生效,这是它口碑的根基。
第三,所见即真实。mock 响应出现在 DevTools 网络面板里,状态码、响应头、耗时都可观察,调试体验和真接口一致。
第四,表达力足够。Express 风格路由加参数、通配符、正则、延迟、自定义 resolver,复杂场景(分页、错误分支、超时)都能精确描述。
第五,MIT 协议加成熟生态。官方文档、示例仓库、Egghead 合作课程,学习资源充足;社区围绕它沉淀了大量实践模式。
第六,TypeScript 重写后的类型体验。v3 起处理器和上下文都有完整类型定义,handler 写错字段编译期就报错,重构时全局可查——对大项目,这一层类型安全省掉的排查时间相当可观。
三、适用场景
后端未就绪时的前端并行开发:按接口文档先写 handlers,前端完整推进。测试异常与边界分支:503、超时、脏数据,一条 handler 的事,不用求后端配合。稳定测试:E2E 不受后端数据变动影响,消灭随机失败。离线开发与演示:没有后端也能完整跑应用、录演示视频。接口契约验证:把 handlers 当作前后端契约的可执行文档。遗留系统改造:给不可控的老接口包一层 mock,安全地重构前端。
四、安装教程
装包:
npm install msw --save-dev浏览器开发需要把 Service Worker 文件放到 public 目录,CLI 一条命令:
npx msw init public/ --save在入口处启动:
import { setupWorker } from 'msw/browser'
import { handlers } from './mocks/handlers'
const worker = setupWorker(...handlers)
worker.start()测试环境用 Node 拦截:
import { setupServer } from 'msw/node'
import { rest } from 'msw'
const server = setupServer(
rest.get('https://api.example.com/user', (req, res, ctx) =>
res(ctx.status(200), ctx.json({ name: 'Mock User' }))
)
)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())常见报错:其一,浏览器里提示找不到 mockServiceWorker.js——漏了 msw init 那一步,或 public 路径写错,确认文件可被直接访问。其二,handler 没生效但控制台无错——检查启动时机,worker.start 应在应用请求发起前完成;测试里确认 server.listen() 在 beforeAll 中。其三,测试之间 mock 互相污染——用 server.resetHandlers() 在 afterEach 复位,单测里的临时 handler 用 server.use()。其四,与 Service Worker 缓存冲突——开发时开启 bypass,或检查浏览器是否缓存了旧版 worker。
五、使用示例
为一个用户中心页面写 handlers。定义请求处理器:
import { http, HttpResponse } from 'msw'
export const handlers = [
http.get('https://api.example.com/user/:id', ({ params }) => {
return HttpResponse.json({
id: params.id,
name: '张三',
role: 'admin'
})
}),
http.post('https://api.example.com/login', async ({ request }) => {
const body = await request.json()
if (body.password === 'wrong') {
return new HttpResponse(null, { status: 401 })
}
return HttpResponse.json({ token: 'mock-jwt-token' })
})
]应用代码照常请求:
const res = await fetch('https://api.example.com/user/42')
const user = await res.json()
// { id: "42", name: "张三", role: "admin" }浏览器 DevTools 网络面板里能看到这条 200 响应,和真实接口别无二致。测试里想验证错误分支,临时覆盖一条:
server.use(
http.get('https://api.example.com/user/:id', () =>
new HttpResponse(null, { status: 503 })
)
)应用收到 503 走降级逻辑,断言页面展示重试按钮——整个过程不需要后端参与,也不改动一行应用代码。
六、常见问题
问:和 Nock、json-server 这类工具怎么选?答:Nock 偏 Node 底层 HTTP 拦截,json-server 是假服务器,MSW 的差异化在网络层抽象加浏览器测试双端统一;多数前端团队的标准组合是开发用 MSW、CI 用它做稳定层。
问:生产环境会被打包进去吗?答:worker 只在开发启动,测试服务器只在测试生命周期内存活,按官方指南接入不会进生产构建。
问:Service Worker 会影响真实请求吗?答:只在 worker 活动期间拦截匹配到的 handler,未匹配的请求正常透传;生产页面不注册 worker。
问:支持 GraphQL 吗?答:支持,官方文档有 GraphQL 专章,query 和 mutation 都能按操作类型定义 handler。
七、总结
MSW 用一层 Service Worker 把「mock」从代码入侵变成了网络层行为,应用零改造、测试全复用、体验与真实接口一致。它解决的不仅是联调排期问题,更是测试稳定性这个更贵的痛点——异常分支随手构造、E2E 不受数据波动影响、一套 handlers 贯穿开发到测试。MIT 协议、TypeScript 重写后的类型体验、成熟的文档生态,让它成为前端工程链路里可以无脑纳入的基础设施。如果你还在用改代码的方式 mock,迁移成本一个下午,收益是长期的。
评论 0
还没有评论,来说两句吧。