说到前端开发中的mock,即假数据,咱们都知道它是用来解决前端开发的闭环调试或测试用的一种技术手段,就是当真正的服务端api没有开发完成,只有接口文档的状况下前端能够先行完成功能开发和测试。mock方案既能够放在服务端,也能够放在本地。javascript
服务端的mock一般须要专门开启一个mock服务,能够进行接口配置和跨域等。前端
本地的mock方案则相对灵活不少,最简单的方式是将mock数据直接注入接受请求返回值的变量中,可是它的缺点很大,侵入性太强,不够灵活,甚至能够说污染了业务逻辑,彻底是给后续挖坑的方案。稍微复杂一点的方案好比mock.js, 经过修改XmlHttpRequest
的默认行为能够拦截ajax,解决了构造随机mock以及侵入太强的问题,可是它的缺点一样明显,真实连调时须要移除相关代码,而且它对于fetch请求素手无策。vue
要作到业务代码无侵入的mock,也很简单。如今的前端项目都是工程化的,大部分都会用到一个本地node服务,好比webpack-dev-server
等,咱们在和服务端连调以前调试网页,看到的全部内容都是本地服务提供的静态响应,api完成以前即使使用代理,绕过跨域也没法有效自测,咱们能够从这个本地服务去入手,经过实现一个中间件去拦截http请求,而后对API接口类型的请求作自定义的响应处理来实现mock。这样的mock方案能够作到业务代码零侵入,连调时只要关闭mock的开关或者移除中间件就能够。java
大多数前端项目本地服务都是依赖express,以它为例,实现一个中间件要如何作呢。这里的关键问题在于URL如何去映射,有两种方案:node
文件映射是指对请求url中的“/”替换,好比api/a/b
这样一个请求,能够映射成api_a_b
这样命名的文件,中间件经过读取文件来响应请求。react
map映射是指没必要按照严格的格式去创建映射文件,而是经过配置一个json
文件去处理,key
表示url
,value
为待返回的响应内容。webpack
文件映射的优势是没必要进行专门配置,对新请求只要创建新的映射文件就行了,可是缺点也很明显,因为文件名是静态的,这种方式难以处理相似api/a/b/10
这样带有pathParam
的get
请求,还有就是文件没法复用,即使两个不一样请求返回的结果是相同的,也须要分别创建各自的mock
文件。相比之下,map映射的方式除了必须的路由配置外,实现能够至关灵活,能够复用mock
。git
下面介绍一下本身写的一个中间件dynamic-mock-express,它具有以下功能和特性:github
1.能够友好地支持Restful API
2.能够自定义url过滤规则,肯定哪些请求不须要mock
3.支持将map映射的value设置为函数,而且能够经过参数接受相对应请求的query,params和body
4.支持将返回值的任意属性或嵌套属性设置为函数,一样能够接受上述参数
5.配置无缓存,修改后不须要重启启动项目就能够生效
6.函数经过接受store对象能够实现动态的、响应式的mock,能够简单模拟真实后端服务
web
1.安装:
npm i dynamic-mock-express -D
复制代码
2.根目录创建mock文件夹,在该文件夹下面创建index.js
,即配置文件,例如:
// index.js
module.exports = {
needMock: true, // 是否开启mock,默认true,连调时设为false 便可,没必要重启项目
prefix: "api", //须要mock的url前缀
tip: true, //为true时匹配不到url时控制台会警告,默认为true
ignore: (url, method) => {
// 能够接受url和请求的method,返回是true的将被过滤,不会被mock处理
},
routes: {
"GET:a/c": require("./mock_1"),
"GET:a/b/:id": (data) => { // 能够接受params参数,data共有四个属性:params、query、body、store
return {
data: "mock_2",
params: data.params,
};
},
"GET:b/:id/:code": ({params}) => { //将请求内容原样返回
return {
id: params.id,
code: params.code
}
},
"POST:b/c": { // 能够直接将value定义为一个json对象
a:1
},
"POST:a/b/c": data => { // 经过data.body能够将post数据自己做为响应
return {
status: true,
body: data.body
};
},
"DELETE:a/b/:id": (data) => { // 支持Restful api
return {
id: data.params.id
};
},
"DELETE:a/b/c/:id": { // 支持属性或嵌套属性定义为函数
a: {
b: 1,
c: (data) => {
return {
id: data.params.id
}
}
}
}
}
};
复制代码
3.挂载中间件
const app = express();
const mock = require("dynamic-mock-express");
app.use(
mock({
mockDir: path.resolve(__dirname, "../mock")
})
);
复制代码
const mock = require("dynamic-mock-express");
new WebpackDevServer(compiler, {
...
setup: (app) => {
app.use(
mock({
mockDir: path.resolve(__dirname, "../mock"), // mock文件夹目录
entry: "index.js" // mock文件夹入口,若是配置文件就叫index.js,能够不配置
})
);
}
}
复制代码
假如发出一个api/a/b/10
的get
请求,mock将会按照如上配置响应以下结果:
{
data: "mock_2",
params: {
id: "10"
}
}
复制代码
4.响应式的动态mock
配置storePath
属性可让mock
具有响应式能力,以下:
const path = require("path");
module.exports = {
needMock: true,
prefix: "api",
storePath: Path.reslove(__dirname, "store"), // 必须是绝对路径
tip: true,
routes: {
"GET:a/b/:id": ({store, params}) => {
return store.data.find(item => {return item.id == params.id});
},
"POST:a/b": ({store, body}) => {
store.data.find(item => {
return item.id == body.id
}).name = body.name;
return {
status: true
};
}
}
}
复制代码
mock/store.js
module.exports = {
data: [
{
id: 10,
name: "zhangsan"
},
{
id: 11,
name: "lisi"
}
]
}
复制代码
用伪代码的形式去发起以下请求:
// pseudocode
get("api/a/b/10").then(res => {
console.log(res) // {id: 10, name: "zhangsan"}
post("api/a/b")
.send({id: 10, name: "wangwu"})
.then(res => {
get("api/a/b/10").then(res =>{
console.log(res) // {id: 10, name: "wangwu"}
})
})
})
复制代码
能够发现post修改数据后再次get的时候结果已经更新,这样咱们就能够经过简单的一些逻辑代码模拟一些调用真实api接口的交互而不用手动去修改mock。
项目地址github.com/silentport/…,欢迎你们提issue。