React 19 + Next.js 16(App Router)项目中集成 MSW

React 19 + Next.js 16(App Router)项目中集成 MSW React 19 Next.js 16App Router项目中集成 MSW 的完整流程。一、安装 MSWnpminstallmsw --save-dev二、初始化 Service Worker 文件MSW 依赖一个mockServiceWorker.js文件来拦截浏览器端的网络请求该文件必须放在public 目录下# 将 mockServiceWorker.js 生成到 public 目录npx msw init public/--save执行后public/mockServiceWorker.js文件会被创建。该文件必须能通过浏览器直接访问如http://localhost:3000/mockServiceWorker.js否则 Service Worker 无法注册。三、创建 MSW 配置文件结构在src/mocks/目录下创建以下文件1.src/mocks/handlers.ts定义请求处理器import{http,HttpResponse}frommsw;// 模拟用户数据constusers[{id:1,name:张三,email:zhangsanexample.com,role:admin},{id:2,name:李四,email:lisiexample.com,role:user},{id:3,name:王五,email:wangwuexample.com,role:user},];exportconsthandlers[// GET /api/users - 获取用户列表http.get(/api/users,({request}){consturlnewURL(request.url);constpageurl.searchParams.get(page)||1;constlimiturl.searchParams.get(limit)||10;returnHttpResponse.json({code:200,data:{page:Number(page),limit:Number(limit),total:users.length,list:users,},});}),// GET /api/users/:id - 获取单个用户http.get(/api/users/:id,({params}){const{id}params;constuserusers.find((u)u.idNumber(id));if(!user){returnHttpResponse.json({code:404,message:用户不存在},{status:404});}returnHttpResponse.json({code:200,data:user});}),// POST /api/login - 用户登录http.post(/api/login,async({request}){constbodyawaitrequest.json();const{username,password}body;if(usernameadminpassword123456){returnHttpResponse.json({code:200,data:{token:mock-jwt-token-abc123,user:{id:1,name:管理员,role:admin},},});}returnHttpResponse.json({code:401,message:用户名或密码错误},{status:401});}),// POST /api/users - 创建用户http.post(/api/users,async({request}){constbodyawaitrequest.json();constnewUser{id:Date.now(),...body};users.push(newUser);returnHttpResponse.json({code:201,message:创建成功,data:newUser},{status:201});}),];2.src/mocks/browser.ts浏览器环境配置import{setupWorker}frommsw/browser;import{handlers}from./handlers;// 创建 MSW worker 实例exportconstworkersetupWorker(...handlers);// 导出启动函数exportasyncfunctionstartWorker(){returnworker.start({// 未匹配到 handler 的请求直接放行发送到真实服务器onUnhandledRequest:bypass,// 指定 Service Worker 文件路径serviceWorker:{url:/mockServiceWorker.js,},// 是否静默模式quiet:false,});}3.src/mocks/server.tsNode.js 环境配置用于测试import{setupServer}frommsw/node;import{handlers}from./handlers;// 创建 Node.js 环境的 server 实例exportconstserversetupServer(...handlers);四、创建 MSW 启动包装组件Next.js App Router 方式由于 Next.js 的 App Router 包含服务端组件Server Component而 Service Worker 只能在浏览器端运行因此需要通过一个客户端包装组件来启动 MSW。src/components/MSWProvider.tsxuse client; import { useEffect, useState } from react; export function MSWProvider({ children }: { children: React.ReactNode }) { const [isMockReady, setIsMockReady] useState(false); useEffect(() { async function enableMocks() { // 只在开发环境启动 MSW if (process.env.NODE_ENV development) { try { const { startWorker } await import(/mocks/browser); await startWorker(); console.log([MSW] Mock Service Worker 已启动); } catch (error) { console.error([MSW] 启动失败:, error); } } setIsMockReady(true); } enableMocks(); }, []); // 等待 MSW 初始化完成后再渲染子组件 if (!isMockReady) { return div加载模拟数据中.../div; } return {children}/; }五、在根布局中集成 MSWProvidersrc/app/layout.tsximport { MSWProvider } from /components/MSWProvider; import ./globals.css; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( html langzh-CN body {/* 用 MSWProvider 包裹整个应用 */} MSWProvider {children} /MSWProvider /body /html ); }六、通过环境变量控制 MSW 开关可选在.env.development中添加NEXT_PUBLIC_API_MOCKINGenabled然后在MSWProvider中增加判断constenableMSWprocess.env.NODE_ENVdevelopmentprocess.env.NEXT_PUBLIC_API_MOCKINGenabled;if(enableMSW){const{startWorker}awaitimport(/mocks/browser);awaitstartWorker();}这样可以灵活地通过环境变量控制是否启用 Mock。七、在客户端组件中使用模拟 APIsrc/app/users/page.tsxuse client; import { useEffect, useState } from react; interface User { id: number; name: string; email: string; role: string; } export default function UsersPage() { const [users, setUsers] useStateUser[]([]); const [loading, setLoading] useState(true); const [error, setError] useStatestring | null(null); useEffect(() { async function loadUsers() { try { // 这个请求会被 MSW 拦截并返回模拟数据 const response await fetch(/api/users?page1limit10); if (!response.ok) { throw new Error(请求失败: ${response.status}); } const result await response.json(); setUsers(result.data.list); } catch (err) { setError(err instanceof Error ? err.message : 未知错误); } finally { setLoading(false); } } loadUsers(); }, []); if (loading) return div加载中.../div; if (error) return div classNameerror❌ {error}/div; return ( div classNameusers-page h1用户列表/h1 ul {users.map((user) ( li key{user.id} strong{user.name}/strong - {user.email} ({user.role}) /li ))} /ul /div ); }八、关键注意事项Service Worker 限制只能在https、localhost或127.0.0.1下工作。如果通过局域网 IP 访问MSW 可能无法正常拦截请求。Next.js 多运行时Next.js 同时有客户端和服务端运行时Service Worker 仅存在于浏览器端。因此 MSW 只能在客户端组件中生效服务端数据获取如generateStaticParams、Server Actions需要使用msw/node的setupServer来模拟。生产环境通过process.env.NODE_ENV development确保 MSW 只在开发环境启动生产构建中不会包含 MSW 代码。零侵入性MSW 在网络层拦截请求前端业务代码无需任何修改即可在模拟数据和真实 API 之间切换——只需开启或关闭 MSW 即可。与 Next.js API Routes 共存MSW 拦截的是浏览器发出的请求不会影响 Next.js 自身的 API Routesapp/api/或pages/api/两者可以共存。