上一篇文章json-server的实践与自定义配置化
提到过,json-server
在我看来不太适用;以前有赞开源的zan-proxy
我也尝试用过,其痛点在于mock数据保存在第三方,这个特性使得公司项目不适合使用zan-proxy
,因此尝试本身搭建一个mock服务——ma-mock
。html
项目中须要对两个开发地址进行代理,部分数据也须要使用mock数据,因此能够参照zan-proxy
作代理和mock的切换按钮,鉴于以前是使用koa
编写后端服务的,因此此次使用koa
编写可用于mock和proxy的可视化服务。前端
虽然说是参照zan-proxy
,可是我仍是保留着本身的想法;首先,与zan-proxy
不一样,zan-proxy
使用浏览器插件进行地址代理,其主要目的是用于调试线上页面,但咱们只在dev环境使用,使用webpack的proxyTable将后端接口都代理到mock服务,由mock服务统一分发代理仍是返回mock数据便可;其次,数据保存在本地,构建一个本地文件增删查改的操做,mock服务只在dev开发中使用,io的损耗其实没有太大的区别;vue
主要有三个功能,分发mock和proxy、提供可视化界面的后端接口、部署前端资源,由于主要是给前端人员使用,因此维护一份全局变量(lib/Global.js)替代redis
。node
三个功能的执行顺序为 分发mock和proxy
-> 返回单页面资源
-> 可视化界面的后端接口
react
'use strict';
const { Logger, fsHandler, Global } = require('../lib/index');
const axios = require('axios');
const pathToRegexp = require('path-to-regexp');
/** * * @param {object} options 配置项 * {object} options.prefix mock数据的url前缀 * @return {function} * */
module.exports = options => {
// 进行中间件参数的配置,最终返回一个中间件函数
let prefix = options.prefix;
// 兼容prefix格式的写法 "/__DEV__/xxx" 或者 "/__DEV__/xxx/"
if (prefix.lastIndexOf('/') + 1 !== prefix.length) {
prefix = prefix + '/';
}
return async function(ctx, next) {
let curPath = ctx.path;
// 不符合prefix的接口地址直接跳过
if (curPath.indexOf(prefix) !== 0) return await next();
let pathArr = curPath.split(prefix);
// 若是prefix以后再也不有path则请求不合法
// 例如:prefix为 __DEV__/pay/但请求路径为http://*/__DEV__/pay/
if (pathArr.length < 2) {
ctx.body = '请求路径不合法';
}
// 判断是否使用MOCK数据
const find = Global.mockList.find(it => {
const re = pathToRegexp(it.url);
return re.test(`/${pathArr[1]}`);
});
// 规则是mock优先级大于proxy
if (find && find.enable) {
ctx.body = handlerMock(find.url.slice(1));
} else if (Global.enableProxy) {
ctx.body = await handlerProxySync(`/${pathArr[1]}`, ctx);
} else {
ctx.body = '未开启proxy';
}
// 此处没有next(),直接返回数据
};
// 用mock数据
function handlerMock(filePath) {
let result = '';
try {
result = fsHandler.getMockFile(filePath);
} catch (e) {
result = e;
}
Logger.debug(result);
return { ...result, type: 'MOCK' };
}
// 后端代理
async function handlerProxySync(api, ctx) {
const options = {
...ctx.request,
url: `${Global.currentProxyUrl}/${api}`,
params: ctx.query,
};
try {
const res = await axios(options);
return { ...res, type: 'PROXY' };
} catch (e) {
return {
message: e.message,
type: 'PROXY',
};
}
}
};
复制代码
中间件使用webpack
// 配置mock
app.use(
mockProxy({
prefix: '__DEV__',
})
);
复制代码
常规后端restful接口,此处略过不讲。ios
由于是开发环境使用,因此没必要部署到nginx上,本身编写了基于koa-static
的koa-spa-static
。nginx
使用方法,配置采用vue打包出来的目录,react可能须要自行按状况修改:git
const spaStatic = required('koa-spa-static');
// 挂在静态资源
app.use(
spaStatic({
matchReg: /^(?!\/api)/, // 不以"/api"开头的接口地址会返回静态资源
root: path.join(__dirname, './dist'), // 静态资源目录
staticReg: /^\/static/, // 前端static资源返回文件,其余返回index.html
})
);
复制代码
使用element-ui2的组件构造,属于简单的组装,基于本身编写的vue cli模板,使用命令github
vue init masongzhi/vue-template-webpack
安装
npm install -D ma-mock
在根目录编写.mamockrc.js
配置文件
const path = require('path');
// 默认配置
module.exports = {
prefix: '/__DEV__',
rootPath: path.resolve(__dirname, './data/mock'),
proxyPath: path.resolve(__dirname, './data/proxy'),
proxyFilename: 'config.json',
};
复制代码
配置webpack proxyTable
// ...省略
module.exports = {
// ...省略
dev: {
// ...省略
proxyTable: {
// 填写 .mamockrc.js的prefix,默认为'/__DEV__'
'/__DEV__': {
target: 'http://localhost:3001', // 接口的域名
// secure: false, // 若是是https接口,须要配置这个参数
changeOrigin: true, // 若是接口跨域,须要进行这个参数配置
}
},
},
}
复制代码
package.json添加script命令
mamock [--port 3001]
咱们在项目跟目录编写了.mamockrc.js
文件,那是怎么在mock服务中读取到这个文件的信息呢,或者说怎样才能将npm包的配置参数写在根目录的文件内。
咱们可使用rc-config-loader
,其余相似包也行,像prettier
就使用editorconfig
和本身编写的editorconfig-to-prettier
。
const rcfile = require("rc-config-loader");
// 会向上遍历.mamockrc or .mamockrc.json or .mamockrc.js or.<product>rc.yml, .mamockrc.yaml
// 咱们须要用到基于跟目录的data文件,因此须要用到__dirname,因此使用js文件
const data = rcfile('mamock');
复制代码
咱们在package.json的scripts编写了mamock --port 3001
命令,是怎么实现的呢。
在package.json添加
"bin": {
"ma-mock": "./bin/mock-proxy.js",
"mamock": "./bin/mock-proxy.js"
}
复制代码
在bin目录添加mock-proxy.js
#!/usr/bin/env node
'use strict';
// 定义用到的参数
const keys = ['port', 'prefix', 'rootPath', 'proxyPath', 'proxyFilename'];
const argvs = process.argv.slice(2);
function getArgv(key) {
const index = argvs.findIndex(it => it === `--${key}`);
return index >= 0 && argvs[index + 1];
}
keys.forEach(key => {
const value = getArgv(key);
if (value) process.env[key] = value;
});
// 执行koa的index.js
require('../server/index.js');
复制代码
启动ma-mock
服务时自动打开浏览器
// index.js
const opn = require('opn');
app.listen(PORT);
opn(`http://localhost:${PORT}`, {app: 'google chrome'});
复制代码
目前仍是比较粗糙,后端只作了简单的joi参数校验,没对mock地址进行url校验;mock数据也只支持基本的application/json
类型,且不支持mock.js
的配置;最重要的,单元测试没补全,后续会慢慢填。
若是有什么建议或者想贡献代码,能够发issue和pr,项目地址: github.com/masongzhi/m…