利用mock提高效率
利用mock提高效率
谈到mock,就不得不讲前后端分离。理想情况下前后端不分离,由全栈的人以product和infrastructure的维度进行开发,效率是最高的。近些年来业务的复杂度越来越高,真正的全栈人才极为难招,企业只能退而求其次,对开发进行分工细化,让每个人做自己最擅长的事,前端负责UI显示和交互,后端负责业务的逻辑、性能等,从而架构上达到更高的效率。同时由于分工的细化,导致前后端的沟通成本增加,代码的控制权通常在后端,一次小的修改可能导致前后端反复沟通,降低开发效率,就产生了前后端分离的方案。前后端分离通过约定好协议,使用约定的协议进行并行开发,将沟通最后阶段放在联调,节省了大量的时间。
分离的表现主要是视图层的控制交给前端,对于一些偏应用类项目,使用ajax请求,前后端各负责自己的部分,直接达到分离状态,而一些展示类系统,受到seo和用户体验的影响,分离只能达到部分分离,如fis方案。不管是哪种状态的分离,都需要进行协议约定,以达到业务的并行开发。对于前端来说,开发的效果更多依赖于数据,想要最大程度的减少联调时间,就需要根据协议生成数据,这也就是mock的需求。
使用mock开发流程
这里借用yapi的流程图(yapi表示mock服务):
首先,前后端进行定制接口,定制完成后各自进行开发。前端的开发者使用mock数据进行开发,开发完成后进行真实环境的联调,找出开发中的问题,再进行测试、上线等流程。
常用的mock方式
1. 代码层硬编码
直接数据写在代码中(或者独立的文件,代码中手动引入),修改代码跳过接口请求,直接使用数据。这种方式的最大问题是耦合性太强,业务代码中混杂了mock数据,每次正式提交都要保证代码数据指向是正确的,否则会产生神奇的结果。同时不是真正发出网络请求,和真实环境有差异。建议仅在没有其他方式的情况下才使用。
2. 代码中针对使用的ajax库拦截
这种方式分为拦截到本地和拦截到其他服务器。相较于硬编码,拦截式降低了mock数据和业务代码的耦合性,只需要引入拦截的代码,将请求转发至本地,至本地文件或其他服务器。这种方案的主要问题是需要针对代码使用的各种库进行定制插件,初期成本高;同时有部分的代码入侵,需要保证入库代码正确。
-
拦截至数据文件:
最典型的是Mock.js。这种方式实现了自由编写数据,灵活性比较大,而且mock的数据文件可以同步至仓库中,下次开发时。缺点是同样非网络请求,真实性不足。 -
拦截并改变请求:
这种方式之所以出现是因为服务器的URL和本机的URL规则不同,需要按照规则进行转换。这种方式与真实情况比较接近,同时产生了跨域,需要服务端提供对应的header。
3. 为所有接口统一添加前缀
这种方式在公用文件中直接添加接口前缀,依赖于特定的库功能(或者达到相同效果的代码结构)。同之前的拦截并改变请求类似,但更为简单一些,只改前缀就可以了,同样有代码入侵的问题。
4. 使用其他工具
这种方式分为两种:使用本地或软件数据和代理转发
-
使用本地或软件数据:
最常用的如:fiddler, charles, whistle等,可以将请求返回指定内容。主要问题是配置比较繁琐,所有操作都在本机,同步困难。 -
代理转发:
这种方式和真实环境极为接近,毕竟纯静态的文件,在服务端也是需要进行代理转发的。可使用的工具就比较多了,使用支持代理转发的开发工具,或者使用上面提到的工具,同样能够将数据转发出去。这种方式最大的优点是:无跨域、能发出真实请求、与业务代码完全隔离。
方案对比
对比以上几种方案,最优的选用代理转发方式,其次是代码拦截方式。拦截至数据文件有个优势就是可以将mock数据和仓库同步,但在不同分支开发时Mock数据可能会冲突。代理转发和拦截至服务器使用最方便,但它需要mockServer。只视开发情况而定。
一些mock用工具
mock的实现非常多,基本上每套完善的前端开发工具都是自带mock的机制。近几年随着nodejs的流行,前端可以非常方便的实现自己的mockServer,所以想列举出所有的还是很困难的,这里介绍一些知名字较高的工具。
1. swagger
- 地址:[https://swagger.io/]
- 介绍: 提到mock,就不得不提到swagger (https://swagger.io/)。它是一个极为流行的一个API设计开发工具,覆盖了从设计到文档到测试部署。它是这样介绍的:
Swagger is the world’s largest framework of API developer tools for the OpenAPI Specification(OAS), enabling development across the entire API lifecycle, from design and documentation, to test and deployment.
在设计RESTFUL类型的API极为有用,它没有专门提供mock服务,但可以提供mock服务的server模板代码,可根据模板自行搭建mock-server。此外,还提供相关的API的JSON结构数据,配合相应的工具来实现类似的效果。
2. Mock.js
- 地址:https://github.com/nuysoft/Mock/tree/refactoring
- 介绍:THX团队出品,本地文件编写mock数据规则,适用于代码库拦截,之前较为流行的一个方案,很多工具都集成了Mockjs语法,目前仓库已经不再更新。
3. rap2
- 地址:https://github.com/thx/rap2-delos
- 介绍:同THX团队出品,是rap0.x的升级版本,使用了nodejs和关系数据库开发,兼容mock.js语法。界面比较简洁,交互友好,支持界面式编辑API。但URL设计与原URL不同,需要使用代码库拦截方式。
4. apiary
- 地址:https://app.apiary.io/
- 介绍:这款在国外用的比较多一些,功能也是比较强大:
Apiary.io平台具有协同设计、即时API模拟、快速生成源码、自动测试和代码调试的开源设计工具,最重要的是可以在线模拟测试,因为该平台具备模拟服务器测试服务,可以把设计好的程序在线测试、验证。
5. easy-mock
- 地址:https://easy-mock.com/
- 介绍:搜车出品,rest api模拟,基于mockjs语法,能够从swagger生成简单数据。
6. yapi
- 地址:http://yapi.qunar.com/getfamiliar.html
- 介绍:这是去哪团队做的,使用了nodejs+mongodb方式开发,主要特性:
- 基于 Json5 和 Mockjs 定义接口返回数据的结构和文档,效率提升多倍
- 扁平化权限设计,即保证了大型企业级项目的管理,又保证了易用性
- 类似 postman 的接口调试
- 自动化测试, 支持对 Response 断言
- MockServer 除支持普通的随机 mock 外,还增加了 Mock 期望功能,根据设置的请求过滤规则,返回期望数据
- 支持 postman, har, swagger 数据导入
- 免费开源,内网部署,信息再也不怕泄露了
rap2和yapi的一些对比
接入mockServer
有了各种极为方便使用的mockServer,想要接入就很简单了。mockServer的一般使用规则:
- URL相同,直接代理转发即可
- URL后半部分相同,直接加上URL前缀即可
- URL规则不同,或需要身份认证,需要拦截转发
拦截式
拦截式针对的是使用一些封装过的ajax库,比如jquery、axios,或者使用fetch库。实现的方式各有不同。如果使用的是支持拦截器模式(如axios),拦截代码就比较简单了,直接在拦截器中改变URL指向即可,指向mockServer或mockjs文件。见以下代码(以下代码是摘自rap或rap2等其他库)。
function wrapAxios(axios) {
var url = ''
var oldRequest = {}
var routePassed = false
axios.interceptors.request.use(function (config) {
url = config.url
config.url = "http://" + ROOT + '/mockjsdata/' + projectId + url;
oldRequest = Object.assign({}, config)
return config;
}, function (error) {
return Promise.reject(error);
});
axios.interceptors.response.use(function (res) {
return res;
}, function (error) {
return Promise.reject(error);
});
}
对于一些不支持拦截器的,或者原生的方法fetch,通过覆盖的方式实现(https://github.com/wenlonghuo/rap2-delos/blob/master/public/libs/fetch.rap.js):
;(function (RAP, fetch) {
if (!fetch) {
console.warn('当前环境不支持 fetch')
return
}
if (!RAP) {
console.warn('请先引入 RAP 插件')
return
}
let next = fetch
let find = (settings) => {
for (let repositoryId in RAP.interfaces) {
for (let itf of RAP.interfaces[repositoryId]) {
if (itf.method.toUpperCase() === settings.method.toUpperCase() && itf.url === settings.url) {
return Object.assign({}, itf, { repositoryId })
}
}
}
}
window.fetch = function (url, settings) {
// ajax(settings)
if (typeof url === 'object') {
settings = Object.assign({ method: 'GET' }, url)
} else {
// ajax(url) ajax(url, settings)
settings = Object.assign({ method: 'GET' }, settings, { url })
}
var match = find(settings)
if (!match) return next.call(window, url, settings)
let redirect = `${RAP.protocol}://${RAP.host}/app/mock/${match.repositoryId}/${match.method}/${match.url}`
settings.credentials = 'include'
settings.method = 'GET'
settings.dataType = 'jsonp'
console.log(`Fetch ${match.method} ${match.url} => ${redirect}`)
return next.call(window, redirect, settings)
}
})(window.RAP, window.fetch)
使用这些插件的方法很简单,直接在html最后添加指向的script标签即可(部分拦截可能需要引入多个标签)。
修改全局URL式
这种情况适合mockServer请求中需要添加baseURL的类型。对于支持baseURL类型的库,设置baseURL即可。如baseURL为:
http://yapi.demo.qunar.com/mock/1304
我们业务代码中请求的api为:
/weather/api
那么我们实际请求的地址是:
http://yapi.demo.qunar.com/mock/1304/weather/api
所以我们应该这么设置(以axios为例):
export default axios = new Axios({
baseURL: process.env.NODE_ENV === 'development' ? 'http://yapi.demo.qunar.com/mock/1304' :
})
如果是不区分环境的情况下,需要在提交前将baseURL设置为空,以免影响仓库代码。
对于不支持baseURL的库,建议封装方法,单独保存baseURL。
代理转发式
代理转发实现的前提是你使用的开发工具支持转发,如果不支持,就需要使用Fiddler、charles等工具进行规则重写。下面举一些例子:
webpack-dev-server中:
proxy: {
"/api": "http://localhost:3000"
}
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true
}
}
注:changeOrigin是http-proxy设置选项,表示在请求头中将host转换为目标服务器的地址或IP,解决服务器出现请求地址找不到的问题。
nginx(应该没人用吧):
location /api {
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_pass http://localhost:11011;
proxy_redirect off;
}
fiddler中在右侧的tab页中选择autoResponder标签页。编辑rule分别输入
REGEX:^https://server\.example\.com/(.*)
http://www.target.com:3838/$1
mock的一些问题
- mock不能替代联调。尽管mock数据再真实,也无法实现和后端接口一样的逻辑,部分逻辑检查或业务的问题只能联调才能发现。
- mock不能替代测试。mock本身是为了加快开发速度,那些对于代码质量要求比较高的项目,mock服务只能提供一些case。
总结
使用mock前提是前后端有一个明确的接口协议,利用合适的工具才能提高开发效率。强大的mock服务可以使你对开发的代码更为自信,即使没有后端,新手就可以通过mock熟悉之前的业务界面,或者查看一些表现特殊的界面,最大程度减少对后端的依赖。
上面介绍的几款mockServer有几种都是使用nodejs开发的。和其他语言开发的mock服务相比,json格式成为书写的主要格式,虽然用起来容易,但书写上并不方便(json5格式在某种程度上增强了书写体验,但相比yaml等格式还是有所不足)。同时业务情况不同,针对性的选择不同的平台,推荐使用rap2和yapi,前者界面更为简洁,操作方便,后者功能更为强大。如果有特殊的需要,可以自己写一个,顺便练练手。
搭建一个属于自己的server
参考
【你是如何构建 Web 前端 Mock Server 的? - 莫池宇的回答 - 知乎
https://www.zhihu.com/question/35436669/answer/235608128】