mock 数据是一件很有意义的事情, 前后端可以并行开发正是得益于 mock 生成的假数据, 趁着有空撸了个轮子 https://github.com/nwa2018/easy-config-mock , 以下记录实现思路以及最终实现的所有代码技术细节
关注点
记录开发过程中的关注点:
如何实现 mock 数据的功能 (技术选型)
使用 mockjs 去生成假数据 (附: mock 规则 http://mockjs.com/examples.html )
使用 express 搭建服务 (附: express 官网 https://www.express.com/ )
如何集成到脚手架中, 或者说工作流中
尽可能设计得简单, 使其很容易集成到现有的脚手架或者工作流中, 实际上 easy-config-mock 也做到了很容易集成, 只需:
- const easyConfigMock = require('easy-config-mock')
- new easyConfigMock({
- // 将配置文件路径传递进去, 服务会自动监听文件变化并重启服务
- path: path.resolve(__dirname, 'mock.config.js')
- })
如何跟项目搭配使用
推荐将 mock 数据的配置文件放在项目的根目录下, 原因在于 mock 数据跟业务是紧密联系的, 丢在一起容易查阅与维护, 如下:
- + vue-preject
- - node_modules
- - src
- mock.config.js // mock 数据的配置文件, 名字仅做示例用
如何支持更复杂的场景
mockjs 的功能很强大, 可以生成随机假数据, 但在业务场景非常复杂的情况下这还不够, 有时为了验证显示逻辑, 期望是可以定制 mock 接口的返回
这是可以做到的, 得益于 express 的中间件, 看一下 easy-config-mock 中的配置文件是怎么写的, 更多说明 https://github.com/nwa2018/easy-config-mock/blob/master/README.md
- // mock.config.js
- module.exports = {
- // common 选项不是必须的, 可以不用有该选项, 内置的配置如下, 当然你也可以更改
- common: {
- // mock 服务的默认端口, 如果端口被占用, 会自动换一个
- port: 8018,
- // 如果你想看一下 ajax 的 loading 效果, 该配置项可以设置接口的返回延迟
- timeout: 500,
- // 如果你想看一下接口请求失败的效果, 将 rate 设置成 0 就可以了, rate 取值范围 0~1, 代表成功的概率
- rate: 1,
- // 默认是 true, 自动开启 mock 服务, 当然你也可以通过将其设置为 false, 关闭掉 mock 服务
- mock: true
- },
- // 普通的 api...
- '/pkApi/getList': {
- code: 0,
- 'data|5': [{
- 'uid|1000-99999': 999,
- 'name': '@cname'
- }],
- result: true
- },
- // 中间件 api(标准的 express 中间件), 这里你可以书写接口返回逻辑
- ['/pkApi/getOther'] (req, res, next) {
- const id = req.query.id
- req.myData = { // 重要! 将返回数据挂载在 req.myData
- 0: {
- code: 0,
- 'test|1-100': 100
- },
- 1: {
- code: 1,
- 'number|+1': 202
- },
- 2:{
- code: 2,
- 'name': '@cname'
- }
- }[id]
- next() // 最后不要忘记手动调用一下 next, 不然接口就暂停处理了!
- }
- }
easy-config-mock 的优点
很容易集成到脚手架或者工作流中, 并且可以自动重启服务
支持自定义中间件, 以满足更为复杂的业务场景
基本不会侵入业务代码, 只需要将接口的请求前缀改成
http://127.0.0.1:mock 端口
配置文件丢在项目里面利于开发与维护
实现的具体技术细节
以下是实现该轮子需要的所有技术细节了, 代码仅简要表达基本思想, 详情内容请看源码 https://github.com/nwa2018/easy-config-mock
如何监听 mock.config.js 文件变化
使用 chokidar 模块
- const chokidar = require('chokidar')
- chokidar.watch(somepath, {
- persistent: true
- }).on('change', _ => {
- // file change...
- // do some logic...
- })
如何实现服务的自动重启
fork 子进程去启动 express 服务, 当配置文件发生变化的时候, 杀掉子进程并重启服务
- const childProcess = require('child_process')
- let child
- // 使用子进程启动 express 服务
- child = childProcess.fork('./server.js', [], {
- encoding: 'utf8',
- execArgv: process.execArgv
- })
- chokidar.watch(somepath, {
- persistent: true
- }).on('change', _ => {
- // 文件发生变化后杀死子进程并重启服务
- child.kill('SIGKILL')
- child = childProcess.fork('./server.js', [], {
- encoding: 'utf8',
- execArgv: process.execArgv
- })
- })
子进程如何读取到配置文件数据
程序给父进程传递的数据子进程是不知道的, 可以利用父子进程之间的通信, 可以参考 child_process 中子进程与父进程之间的通信与断开连接 https://www.zybuluo.com/frank-shaw/note/505788
- // 给子进程传递数据
- child.send({
- path: path
- ...
- })
- // 子进程接收到数据
- process.on('message', ({ path, ... }) => {
- delete require.cache[path]
- // 这里, 拿到了 mock 数据的配置项
- const options = require(path)
- })
require 是有缓存的, 需要先删除 require 的缓存, 再去重新获取配置文件的数据
如何去模拟 jsonp 的请求
首先得知道该请求是否是 jsonp, 检测请求链接是否带有 callback 参数
- let dataType
- app.use((req, res, next) => {
- dataType = req.query.callback ? 'jsonp' : 'json'
- next()
- })
如何延迟接口的返回
有时, 我们编写了 loading 的效果并想验证一下
- const delayRes = (time) => (req, res, next) => {
- setTimeout(function() { next() }, time)
- }
- // 给接口增加 1 秒延迟
- app.use(delayRes(1000))
如何让接口返回失败
有时, 我们想看下断网或者服务器出错时的效果
- const successRate = (rate) => (req, res, next) => {
- if (rate> Math.random()) return next()
- return next(500)
- }
- // 100% 返回 500 错误
- app.use(successRate(0))
- app.use((err, req, res, next) => {
- res.status(500).json({ status: 0, code: 500, msg: 'Server Error' })
- })
如何允许跨域
访问的非 jsonp 的 mock 接口是跨域请求
(协议, 域名, 端口三者相同才为同域)
, 跨域请求是禁止的, 会报错, 这里需要设置为允许跨域
- const crossDomain = () => (req, res, next) => {
- res.header("Access-Control-Allow-Origin", "*");
- res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
- res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
- if (req.method === 'OPTIONS') res.status(200) // 让 OPTIONS 快速返回
- next();
- }
- app.use(crossDomain())
最终: 路由创建
- const { mock } = require('mockjs')
- // options 是配置文件里面的 api 信息
- Object.keys(options).forEach(path => {
- const data = options[path]
- // 如果是自定义中间
- if (typeof data === 'function') app.use(data)
- app.use(path, (req, res, next) => {
- // req 中带有 myData 的话说明是自定义中间件, 否则是普通的 mock api
- const rsp = req.myData ? mock(req.myData) : mock(data)
- res.status(200)[dataType](rsp)
- })
- })
补充: easy-config-mock 的 webpack 版插件
https://www.npmjs.com/package/easy-mock-webpack-plugin
其实直接用 easy-config-mock 就可以了
参考链接
- https://github.com/yyfrontend/vipm-cli
- MockWebpackPlugin https://github.com/MarxJiao/mock-webpack-plugin/blob/master/readme-zh.md
都看到这了, 赏个赞呗~源码地址 https://github.com/nwa2018/easy-config-mock
本文完.
来源: https://juejin.im/post/5af58c5cf265da0b8a67af25