导读:本期聚焦于吴凌云创作的《React项目如何模拟慢速API?用Mock Service Worker(MSW)拦截请求实现网络延迟模拟》,敬请观看详情。前端页面在接口秒回的情况下往往看不出问题,可一旦后端响应变慢,加载状态缺失、重复提交、界面卡死等隐患就会集中暴露。Mock Service Worker(MSW)通过Service Worker在网络层拦截请求,只需几行配置就能给任意接口加上自定义延迟,还支持针对不同接口设置差异化的响应时间。本文介绍MSW的基本原理,讲解在React项目中如何安装启用、编写handler、用delay函数模拟慢速API,以及如何按接口粒度控制延迟时间,帮助你把弱网场景纳入日常开发与测试流程。

大多数前端开发者平时联调时,本地接口响应速度都在几十毫秒以内,页面看起来一切正常。但真实用户的网络环境千差万别,一个接口卡上三秒,按钮没有loading、列表没有骨架屏、用户连点三次提交的问题就全冒出来了。要在开发阶段暴露这些问题,最直接的办法就是把API故意变慢。Mock Service Worker(简称MSW)正好提供了这个能力:它在浏览器内部用Service Worker拦截网络请求,不需要改动任何业务代码,就能给指定接口加上几秒的延迟。

React项目如何模拟慢速API?用Mock Service Worker(MSW)拦截请求实现网络延迟模拟

一、MSW为什么适合做慢速API模拟

模拟慢接口的手段其实不少:可以在DevTools的Network面板里限速,可以后端加个sleep,也可以自己写个mock服务器。但这些方案各有短板。DevTools限速是全局性的,会把所有请求都拖慢,连静态资源一起遭殃,而且无法针对单个接口设置精确的延迟时间。后端改代码则依赖后端配合,节奏不受前端控制。自建mock服务器又多了一套服务需要维护,接口路径、端口、代理配置都要跟着改。

MSW的思路完全不同。它利用浏览器原生的Service Worker API,在真实的网络层注册一个拦截脚本。当页面发起fetch或XMLHttpRequest请求时,这个脚本会先捕获请求,判断是否有对应的handler:有就返回mock响应,没有就放行到真实服务器。整个过程发生在浏览器内部,请求地址、URL、请求头都与真实环境一致,前端代码完全无感知。

更重要的是,MSW对延迟的控制非常精细。它提供了delay()工具函数,既可以写死一个固定毫秒数,也可以用delay(2000)这种形式精确模拟,甚至支持'real'模式根据实际网络特征随机产生延迟。你可以只让订单提交接口慢三秒,其他接口照常秒回,这种按接口粒度的控制是限速工具做不到的。

二、在React项目中安装并启用MSW

先安装依赖。开发环境只需要msw本体:

npm install msw --save-dev
# 或者
yarn add msw --dev

安装完成后,执行MSW提供的CLI命令生成Service Worker脚本文件,这个文件需要放在项目的public目录下,浏览器注册时才能找到它:

npx msw init public/ --save

这条命令会在public目录下生成一个mockServiceWorker.js文件,它就是运行在浏览器里的拦截脚本,通常不需要修改也不需要提交逻辑变更。接下来创建handler文件,定义要拦截的请求和对应的mock响应:

// src/mocks/handlers.js
import { http, HttpResponse, delay } from 'msw'

export const handlers = [
  // 拦截获取用户列表的GET请求,延迟3秒返回
  http.get('/api/users', async () => {
    await delay(3000)
    return HttpResponse.json([
      { id: 1, name: '张三' },
      { id: 2, name: '李四' }
    ])
  }),

  // 拦截提交订单的POST请求,延迟5秒返回
  http.post('/api/orders', async () => {
    await delay(5000)
    return HttpResponse.json(
      { orderId: 'A1024', status: 'created' },
      { status: 201 }
    )
  })
]

然后创建worker并导出,注意onUnhandledRequest建议配置成'bypass',这样没有匹配handler的请求会直接放行到真实后端,避免干扰正常联调:

// src/mocks/browser.js
import { setupWorker } from 'msw'
import { handlers } from './handlers'

export const worker = setupWorker(...handlers)

最后在应用入口文件里,仅在开发环境下启动worker:

// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

async function enableMocking() {
  if (process.env.NODE_ENV !== 'development') {
    return
  }
  const { worker } = await import('./mocks/browser')
  await worker.start({ onUnhandledRequest: 'bypass' })
}

enableMocking().then(() => {
  ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  )
})

这里有个细节值得注意:worker必须在应用发起任何请求之前启动,所以要把渲染逻辑放到enableMocking()的then回调里,或者至少保证启动顺序在前。生产构建中通过环境变量判断直接跳过,mock代码不会影响线上包体积,因为用了动态import,打包工具会自动分割。

三、用delay函数精细化控制延迟场景

MSW的delay()函数比想象中灵活。除了传固定毫秒数,还支持'infinite'让请求永远挂起,用来测试超时逻辑和用户取消操作特别有用。比如要验证前端的请求超时兜底是否生效,可以这样写:

import { http, HttpResponse, delay } from 'msw'

export const timeoutHandlers = [
  // 模拟超时:请求永不返回
  http.get('/api/config', async () => {
    await delay('infinite')
  }),

  // 模拟真实网络的随机抖动,延迟在800到3000毫秒之间
  http.get('/api/dashboard', async () => {
    await delay(800 + Math.random() * 2200)
    return HttpResponse.json({ revenue: 98210, orders: 341 })
  })
]

除了延迟,错误场景的模拟也很重要。delay经常和错误响应组合使用,比如模拟弱网下先超时再报错的情况:

// 慢接口 + 服务端错误
http.get('/api/report', async () => {
  await delay(4000)
  return new HttpResponse(null, { status: 500 })
})

// 模拟网络中断
http.get('/api/payment', async () => {
  await delay(2000)
  return HttpResponse.error()
})

另一个实用技巧是配合环境变量控制延迟开关。团队协作时不可能让所有人都忍受慢接口,可以在启动命令里加一个参数:

// 在handlers.js中读取开关
const slowMode = process.env.REACT_APP_SLOW === 'true'

http.get('/api/users', async () => {
  if (slowMode) {
    await delay(3000)
  }
  return HttpResponse.json([...])
})

package.json中增加一条脚本"start:slow": "cross-env REACT_APP_SLOW=true react-scripts start",需要压测弱网体验时执行npm run start:slow,日常开发继续用普通的npm start,两套环境互不干扰。

四、在组件测试中复用同一套慢速mock

MSW的一大优势是同一份handler既能跑在浏览器里,也能跑在Vitest或Jest的Node环境中,只需要用setupServer替换setupWorker。这对测试loading状态、防重复提交这类交互逻辑非常有价值:

// src/mocks/server.js
import { setupServer } from 'msw/node'
import { handlers } from './handlers'

export const server = setupServer(...handlers)

在测试配置文件中全局启用,并在每个用例结束后重置handler,避免用例间相互污染:

// src/setupTests.js
import { server } from './mocks/server'

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

这样写一个测试用例验证按钮在慢接口期间的禁用状态:

import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import OrderForm from './OrderForm'

test('提交订单期间按钮应处于禁用状态', async () => {
  render(<OrderForm />)
  const button = screen.getByRole('button', { name: '提交订单' })
  await userEvent.click(button)
  expect(button).toBeDisabled()
  // 等待延迟5秒的响应返回后按钮恢复
  expect(await screen.findByText('下单成功')).toBeInTheDocument()
  expect(button).toBeEnabled()
})

总结一下,MSW把慢速API模拟变成了前端可以独立掌控的事:安装一次、写好handler,开发环境、测试环境共享同一套延迟定义,无需后端配合,不污染真实请求。建议把delay写进常用的mock模板里,让弱网场景成为日常自测的常规动作,而不是上线后才补救的意外情况。

ReactMock Service WorkerMSW修改时间:2026-09-03 04:10:47

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49320.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。