背景
前端项目经常需要在本地使用 mock 数据,因为有些特殊数据会进行一些特殊的处理。在项目运行的本地环境下,如果使用dev环境的后端接口会需要使用后端真实的数据,很麻烦,所以需要进行mock。
选择mock service worker的原因是因为它可以模拟网络延迟,模拟状态码,几乎覆盖了项目所需要的所有 mock情况。
安装步骤
安装库
指令如下:
npm install msw --save-dev
# or
yarn add msw --dev
建立 mock 文件夹
在src目录下定义mock目录,里面新建一个handlers文件。如果项目的服务很多的话,可以为每个类型的服务请求都建立一个handlers,最后统一导出。
mkdir src/mocks
touch src/mocks/handlers.js
定义 mock 数据
这一步就是可以通过在前端定义需要的数据,来达到 mock 的目的了。 文档里给的示例如下:
// src/mocks/handlers.js
import { rest } from 'msw'
export const handlers = [
rest.post('/login', (req, res, ctx) => {
// Persist user's authentication in the session
sessionStorage.setItem('is-authenticated', 'true')
return res(
// Respond with a 200 status code
ctx.status(200),
)
}),
rest.get('/user', (req, res, ctx) => {
// Check if the user is authenticated in this session
const isAuthenticated = sessionStorage.getItem('is-authenticated')
if (!isAuthenticated) {
// If not authenticated, respond with a 403 error
return res(
ctx.status(403),
ctx.json({
errorMessage: 'Not authorized',
}),
)
}
// If authenticated, return a mocked user details
return res(
ctx.status(200),
ctx.json({
username: 'admin',
}),
)
}),
]
可以看到该文件导出了一个handler,这个handler里面定义了两个 mock 接口,分别是/login的 POST请求,和/user 的 GET 请求。每一个rest的前一个参数是路由,后一个参数是一个箭头函数。
该箭头函数有三个参数,分别是req,res,ctx,官方文档对其的定义如下:
- `req`, an information about a matching request;
- `res`, a functional utility to create the mocked response;
- `ctx`, a group of functions that help to set a status code, headers, body, etc. of the mocked response.
可以看到这三个参数的作用分别是:
- 本地发送请求的对应
- 创建本地 mock 响应的函数,用来创建具体 mock 的状态码,网络请求 headers
- 返回的具体内容等的响应内容
能够获取req的好处就是,可以在本地动态地根据请求来返回不同的响应,比如我现在mock了一个接口,这个接口需要一个参数,当参数是 1 的时候返回成功,即视为一个成功的请求,http 状态码是 200;当参数是其他的时候返回失败,即视为一个失败的请求,http 状态码是 400。 举例如下:获取参数param可以通过下面这样的方式:
const param = new URL(req.url).searchParams.get('param');
然后在箭头函数中进行param的判断:
if (param === 1) {
return res(ctx.status(400), ctx.json({}));
} else {
return res(ctx.status(200), ctx.json({}));
}
集成你的 mock 数据
这一步我的项目是使用react和vite搭建的,所以我的集成方法会和这两个框架有关。 Mock Service Worker是通过注册 Service Worker 来拦截请求的,但我们不需要写任何worker的代码,而是通过复制一份Mock Service Worker分发的worker来实现的。 而我们复制文件的指令如下:
npx msw init <PUBLIC_DIR> --save
如果项目是通过Create React App 建立的,那么该指令对应的就是:
npx msw init public/ --save
做完这个之后其实可以看到,我们的package.json文件里面多了一段内容:
"msw": {
"workerDirectory": "public"
}
这个应该就是msw为我们添加的。
新建一个worker实例
在 mock 文件夹下面建一个browser.js文件:
touch src/mocks/browser.js
内容如下:
// src/mocks/browser.js
import { setupWorker } from 'msw'
import { handlers } from './handlers'
// This configures a Service Worker with the given request handlers.
export const worker = setupWorker(...handlers)
这段代码的内容可以视为在新建一个worker
使用 mock 数据
最后一步就是进行环境的判断,这一步可以用不同的方法进行,我是通过在vite的dev环境里面配置的vite变量来获取当前项目运行的环境的。
当判断出项目运行在本地环境的时候,就使 msw 生效。
官方示例代码如下:
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
if (process.env.NODE_ENV === 'development') {
const { worker } = require('./mocks/browser')
worker.start()
}
ReactDOM.render(<App />, document.getElementById('root'))
这个判断一般是在app文件,index文件,main文件里面做的。
这一步完成之后,就可以在本地运行项目,使用自己的 mock 数据了。
彩蛋
最近在给项目安装msw,但是mock没有生效,一直404,这里放上我的调试过程。
- 在 public 文件夹下面的 mockServiceWorker 文件中加上打印,发现有打印内容,说明Service Worker是正常的
- 查看请求,发现url没有异常
- 因为项目在localhost之后加了一层url,对比之前的 mock 写法,发现是在react main文件配置的时候写错了,正确代码如下:
if (ENV === 'local') {
await worker.start({
serviceWorker: {
url: `/${[第二层路由]}/mockServiceWorker.js`,
},
});
}
refer: