Mock Server(仿造服务器),即用于仿造后端接口的模拟 HTTP 服务器。它是一个简单的 HTTP 服务,在后端未准备好的情况下,它可以为前端提供一个可用的 API 服务。在工程时间做的好的项目里,它几乎是前后端分离应用的标准配置。phodal 前端架构-从入门到微前端
最开始接触使用 mock 数据的时候,我以为的 mock 数据的使用和理解与上面引用的类似,都是基于一种契约精神,前后端约定契约 API ,然后各自并行开发实现 mock server,最后用极短的时间进行连调测试。但是在实际使用项目中(这里仅以我接触到的项目作为判断依据),发现并无法到达预想中的顺畅感。
有几个基本的原因:1、 后端在实现的时候,会根据对接的各种平台对业务和逻辑进行修改,切这部分修改并不会及时的同步到前端 2、就算这部分变更及时同步到了前端,前端并无法及时的进行修改,基本上都会认为这部分数据是假数据,不够重视,也和手上还有大量的页面编写,有关 3、 临时的调整版本计划,比如最开始我们约定了编写玩30个页面,再进行连调,产品说要先发两个页面给用户看看 4、 后端接口懒得测试,期望前端帮忙测试
这会导致我们在开发中的过程中频繁的修改请求数据相关的逻辑,最大的问题是可能会导致错误的工时或工期的评估,比如本来就很少的测试时间,被压缩的更少了。预想中的情况是,修改一个请求 prefix 就能完成连调工作,结果确实每一个 API 都要修改。但是工时和工期却只剩两天?
经过多个项目的总结和反馈,最终我们将 mock server 定位为一种接口的补救方案。
以下通过实现一个简单的 Mock Server 来详细的描述:如何补救。
新建一个 express 项目
npm install -g express-generator express --view=hbs /tmp/foo
模拟接口
当服务端,某一个接口挂了,但是你却需要请求这个接口来完成你的页面逻辑时,编写 mock 服务,比如
import express,{ Request, Response, Router, NextFunction } from 'express'; const app = express(); const router = Router(); router.get('/', function (req: Request, res: Response, next: NextFunction) { res.send({ title: 'Mock Server' }); }); app.use('/hello', router); const server = http.createServer(app); server.listen(3000);
这样我们访问 http://localhost:3000/hello
就能获取到返回的 mock 数据了。这样只需在前端项目中,修改一下请求地址就好。
使用第三方库生成模拟数据
mock 数据太难写,比如我们的契约约定了字段名,但是字段的内容,也是一件让人很纠结的事情。懒一点的都是根据字段类型,比如 string 就写 text,number 就写 1,导致渲染出来的页面很丑,总有一种 CSS 写的有误的错觉。
这里我们有两种选择,一种是引入 mock.js ,一种是引入 faker.js
res.send({ title: 'Mock Server' });
请求代理
懒得写 mock 数据,可以从一些在线的 mock 服务里面获取,比如我常常去偷 pro 的数据。实现起来也很简单
yarn add http-proxy-middleware
import { createProxyMiddleware } from 'http-proxy-middleware'; // 略 app.use( '/api', createProxyMiddleware({ target: 'https://proapi.azurewebsites.net/', changeOrigin: true, pathRewrite: { '^': '' }, }), );
这样子写,就可以将 http://localhost:3000/api
的请求代理到 https://proapi.azurewebsites.net/api
,这里要简单的理解,代理服务,代理的只是服务,并非请求地址转移,即你从调试工具中看到的请求地址,并不是最终获取数据的地址。
跨域访问
如果是前端和后端之间的跨域访问问题,可以通过上面提到的请求代理处理。但是如果是前端和 mock server 之间存在跨域访问呢?其实也是一样的,使用 cors 处理。实现也是非常简单,直接使用对应的库就好了
// yarn add cors import cors from 'cors'; app.use(cors());
提供部署容器
服务端没有多余的容器,或者说申请的正式服务器还没有批下来。这时候就可以临时的充当前端部署的容器。将前端打包后的产物,放到 mock server 中,让用户访问。将前端编译后的产物,放到 public 文件夹中,然后添加配置
app.use(express.static(path.join(__dirname, 'public')));
访问 http://localhost:3000/
就能访问到 public/index.html
Gzip
用户反映我们的包太多的了,首屏有个文件 1M 多。和上面的方案实现都一样,用 compression 就能解决,1M 多最终访问只需要几百 k ,感觉效果还行。
import compression from 'compression'; app.use(compression());
上面提到的问题,你是不是也有似曾相似的感觉呢?之所以称之为补救方案,更多的是遇到问题解决问题。
比如我们在 umi 的项目中,通过约定 mock 文件夹下的文件,会被自动识别为 mock 服务,我们也可以引用进来。
// getMockData 方法,来自 umi 源码 https://github.com/umijs/umi/blob/master/packages/preset-built-in/src/plugins/commands/dev/mock/utils.ts const ignore = [ // ignore mock files under node_modules 'node_modules/**', // ...(userConfig?.mock?.exclude || []), ]; const { mockData } = getMockData({ cwd: `${winPath(process.cwd())}/dist`, // 这里要写对你的 mock 文件夹的所在路径 ignore, // registerBabel, }) app.use((req, res, next) => { const match = mockData && matchMock(req, mockData); if (match) { return match.handler(req, res, next); } else { return next(); } });
TS 编译
如果你的项目是 ts 的项目,但是你的 mock 服务是 js 的,(使用 express 初始化的项目就是 js 的) 这时候,你可以将 mock server 改成 ts 的然后一起编译,也可以仅仅把你的 mock 文件夹编译成 js,再拷贝到 mock server 里面,简单的是现实是,增加 tsconfig 文件,主要就是 target 和 outDir,然后使用 tsc 编译。
{ "compilerOptions": { "target": "es5", "moduleResolution": "node", "jsx": "preserve", "skipLibCheck": true, "esModuleInterop": true, "experimentalDecorators": true, "strict": true, "forceConsistentCasingInFileNames": true, "noImplicitReturns": true, "suppressImplicitAnyIndexErrors": true, "skipDefaultLibCheck": true, "declaration": true, "outDir": "dist", }, }
以上方案的演示代码在 mock-server正在编写 umi 的 plugin 插件,所以上面的库,可能会根据最终需要,进行修改。感谢阅读!欢迎讨论!👍