等春风得意,等时间嘉许
我们通常通过create-react-app
脚手架创建出来的项目是没有mock数据的。但通过mock数据可以帮助我们快速测试,独立于后端开发,降低前后端之间的耦合度。下面带大家实现一个简单的mock数据,也可以在此基础上根据自己的业务进一步扩展
WebpackDevServer
我们先通过cra
脚手架创建一个React项目,并暴露出原本的webpack配置文件
npx create-react-app cra_mock
npm run eject
npm install mockjs
当我们执行npm start
时,它会执行scripts/start.js
下面的脚本。它会读取webpackDevServer.config.js
下面的文件,从而创建一个WebpackDevServer的实例
const WebpackDevServer = require('webpack-dev-server');
const createDevServerConfig = require('../config/webpackDevServer.config');
const serverConfig = {
...createDevServerConfig(proxyConfig, urls.lanUrlForConfig),
host: HOST,
port,
};
const compiler = createCompiler({
appName,
config,
urls,
useYarn,
useTypeScript,
webpack,
});
const devServer = new WebpackDevServer(serverConfig, compiler);
webpack-dev-server
底层是express
+ webpack-dev-middleware
express是基础,webpack-dev-middleware是中间件,以监听模式启动 webpack,将编译后的文件输出到内存(使用fs-memory
),通过webpack的HRM(热替换 Hot Module Replacement)API便于本地开发的实时更新。
const express = require('express');
const webpackDevMiddleware= require('webpack-dev-middleware');
const app = express();
const devServer = app.use( webpackDevMiddleware(config) )
看到这里,我们是不是就能想到,既然express可以使用webpack-dev-middleware的中间件,那我们是不是也可以自定义一个中间件,来扩展WebpackDevServer的能力呢?
当然,我们可以自定义一个中间件,来进行本地代理mock数据
自定义中间件
webpack.docschina.org/configurati…
我们修改一下webpackDevServer.config.js
配置文件,通过devServer.setupMiddlewares
这个接口,可以自定义我们的中间件。
setupMiddlewares(middlewares, { app }) {
app.use(mockServiceMiddleware());
return middlewares;
}
mockServiceMiddleware实现
我们要实现的中间件功能如下:
第一步:我们要判断请求的url中是否包含mock,如果包含mock,我们才走本地代理
const mockServiceMiddleware = () => {
return (request,response,next) => {
const parsedUrl = url.parse(request.url, true);
const pathname = parsedUrl.pathname;
if (/^/mock/.test(pathname)) {
return proxyToLocal(request, response);
}
next();
}
}
第二步:格式化request数据,获取post请求中body的信息,放入request.body中
const proxyToLocal = (request, response) => {
const parsedUrl = url.parse(request.url, true);
request.query = parsedUrl.query;
const data = [];
request.on('data', trunk => {
data.push(trunk && trunk.toString());
});
request.on('end', trunk => {
if (trunk) {
data.push(trunk.toString());
}
request.body = data.join('');
mockLocalData(request, response);
});
}
第三步:通过url获取本地mock数据文件
// mock数据存放的目录
const responsePath = path.resolve(process.cwd(), 'mock/response');
const findMockFilePath = (pathname) => {
if (pathname.indexOf('/mock') === 0) {
pathname = pathname.slice(5);
}
const paths = pathname.split('/');
const resDataPath = responsePath + '/' + paths[1] + '.js';
return resDataPath;
}
第四步:读取mock数据
function getMockData(tplPath, options) {
const tpl = require(tplPath);
if (typeof tpl === 'function') {
return Mock.mock(tpl(options));
}
else if (typeof tpl === 'object') {
return Mock.mock(tpl);
}
return {
code: 1,
message: 'mock文件需要是object或者function'
};
}
第五步:通过response.json将mock数据返回
const mockLocalData = (request, response) => {
const parsedUrl = url.parse(request.url, true);
const pathname = parsedUrl.pathname;
const body = request.body;
const query = parsedUrl.query;
const options = {
pathname, body, query
};
const mockfile = findMockFilePath(pathname);
const data = getMockData(mockfile, options);
response.json(data);
}
Demo
我们来写两个请求来验证一下,分别是get请求和post请求
function App() {
const getRequest = () => {
fetch('/mock/list').then((data) => {
console.log(data);
})
}
const postRequest = () => {
const data = {
'name': 'cwl',
}
fetch('/mock/table',{
method:'POST',
body: JSON.stringify(data),
})
}
return (
<div className="App">
<header className="App-header">
<button onClick={getRequest}>
get请求
</button>
<br />
<button onClick={postRequest}>
post请求
</button>
</header>
</div>
);
}
export default App;
我们的mock数据统一存放在mock/response
这个文件夹下面,新建list.js
和post.js
两个文件
// list.js
module.exports = {
"errmsg": "",
"code": 0,
"data": {
"name": "cwl",
"age": 21,
}
}
// post.js
module.exports = (options) => {
const {body} = options;
const data = JSON.parse(body);
const {name} = data;
// 根据post中body请求信息,来进行不同的mock数据
if(name === 'cwl') {
return {
"code": 0,
"msg": "success",
"data": {
"review": "handsom"
}
}
}else {
return {
"code": 0,
"msg": "success",
"data": {
"review": "ugly"
}
}
}
}
源代码如下:
ToDo
- 错误及边界情况处理,如文件查询失败等
- 模块化,如何根据业务划分mock模块化,划分出不同的mock文件
- 能否利用缓存,将读取后的文件放入缓存中,提升性能