想象一下这样的场景:你正在开发一个电商网站的前端页面,后端同事告诉你"用户登录接口还没做好,可能要等两周"。这时候你有两个选择——要么干等着,要么用 MSW 自己造一个模拟接口。我选择后者已经三年了,现在团队的新项目从第一天就开始用 MSW,开发效率提升了至少 40%。
MSW(Mock Service Worker)本质上是一个浏览器级别的请求拦截器。它不像传统的 mock 方案那样需要启动一个本地服务器,而是直接在浏览器运行时层面拦截请求。这意味着你可以在不修改任何业务代码的情况下,让前端应用"以为"自己在和真实后端通信。
去年我们团队接手一个金融项目时,后端 API 文档写了 30 多页却连一个可用接口都没有。靠着 MSW,我们在一周内就完成了所有前端页面的开发和单元测试。当后端终于交付时,我们只需要关闭 mock 开关,所有功能立即就能对接真实数据。
传统的 mock 方案通常需要:
而 MSW 只需要三行代码就能工作:
javascript复制// 初始化 worker
import { setupWorker } from 'msw'
const worker = setupWorker(...handlers)
worker.start()
我在最近的项目中做过对比:使用传统的 Express mock 服务器需要 15 分钟配置环境,而 MSW 从安装到运行第一个 mock 接口平均只需 3 分钟。对于需要快速迭代的敏捷团队来说,这个时间差意味着每天能多完成 2-3 个需求。
MSW 使用 Service Worker 技术,这意味着:
上周我调试一个文件上传进度条时,用 MSW 轻松模拟了不同网速下的上传过程:
javascript复制http.post('/upload', async () => {
// 模拟慢速上传(2秒延迟)
await delay(2000)
return HttpResponse.json({ success: true })
})
好的 mock 不是随意返回数据,而是要建立前后端契约。我们团队现在会这样组织 handlers:
code复制/mocks
├── handlers.js # 基础接口定义
├── scenarios.js # 各种测试场景
└── db.js # 模拟数据库
一个完整的用户模块 mock 示例:
javascript复制// db.js
export const users = new Map([
['1', { id: '1', name: '测试用户' }]
])
// handlers.js
http.get('/users/:id', ({ params }) => {
const user = users.get(params.id)
return user
? HttpResponse.json(user)
: new HttpResponse(null, { status: 404 })
})
在开发支付功能时,我们需要测试多种情况:
通过 MSW 的场景功能,可以在浏览器控制台实时切换:
javascript复制// scenarios.js
export const paymentScenarios = {
success: rest.post('/pay', (_, res, ctx) => {
return res(ctx.json({ status: 'paid' }))
}),
insufficientFunds: rest.post('/pay', (_, res, ctx) => {
return res(ctx.status(402))
})
}
通过类型生成工具,我们可以让 mock 数据也享受类型提示:
typescript复制// 根据后端swagger生成类型
type User = {
id: string
name: string
}
http.get<User[]>('/users', () => {
return HttpResponse.json([
{ id: '1', name: '类型安全的mock' }
])
})
在最近的一次大促准备中,我们用 MSW 模拟了 10 万级商品列表:
javascript复制http.get('/products', () => {
// 生成1000条测试数据
const mockProducts = Array.from({ length: 1000 }, (_, i) => ({
id: i,
name: `商品${i}`,
price: Math.random() * 100
}))
return HttpResponse.json(mockProducts)
})
这帮助我们在没有后端配合的情况下,提前发现了虚拟滚动组件的性能瓶颈。
很多开发者认为 MSW 不能 mock 文件上传,其实完全可以:
javascript复制http.post('/upload', async ({ request }) => {
const formData = await request.formData()
const file = formData.get('file')
// 模拟处理过程
await new Promise(resolve => setTimeout(resolve, 500))
return HttpResponse.json({
url: `https://cdn.example.com/${file.name}`
})
})
通过结合 localStorage 实现持久化会话:
javascript复制http.post('/login', async ({ request }) => {
const { username } = await request.json()
localStorage.setItem('currentUser', username)
return HttpResponse.json({ token: 'mock-token' })
})
http.get('/profile', () => {
const user = localStorage.getItem('currentUser')
return user
? HttpResponse.json({ username: user })
: new HttpResponse(null, { status: 401 })
})
在我们的 CI/CD 流水线中,MSW 扮演着关键角色:
特别是对于微前端架构,各个子应用可以独立开发:
javascript复制// 主应用注册通用handler
setupWorker(
...authHandlers,
...productHandlers
)
// 子应用只需关注自己的路由
setupWorker(...checkoutHandlers)
三年前我第一次接触 MSW 时,它只是个简单的 mock 工具。如今它已经成为我们团队前端开发的基础设施。从新人入职到日常开发,从代码评审到自动化测试,MSW 贯穿了整个研发生命周期。最让我惊喜的是,当我们把完善的 mock 方案交给后端团队时,他们反而更清楚该如何设计 API 了——因为前端已经用 mock 定义好了理想的接口规范。