[关闭]
@xiaoqq 2016-12-09T13:20:26.000000Z 字数 4261 阅读 2830

前端Mock数据与请求

Mock


一、引言

大公司的岗位泾渭分明,大家各司其职,一起把事情做好。当项目开始时,前后端同时进行,那么问题来了,后端还未开发完,无法给前端提供真正的接口,而前端需要调试怎么办?就只好自己伪造(Mock)数据和请求。

Mock数据

的方法有多种

  1. 根据接口文档,手工Mock数据;这种方式不需要额外的方式辅助,简洁快速,可以随意Mock自己想要的数据,缺点是无法生成大量的数据,数据是死的,没有随机性;

  2. 根据接口文档,定义Mock数据的模板,使用生成Mock数据的工具(比如:faker.jsMock.js);优点是快速高效,修改起来方便,缺点当后端接口改变时需要再次修改Mock数据模板,还有就是遇到某种极端的情况无法Mock(比如:需要一个数组的数据加起来等于100%);

  3. 在接口文档中定义好模板规则,当后端修改接口时,会自动根据接口生成新的Mock数据(比如:swagger);缺点是操作比较复杂,而且需要后端人员配合。

有了Mock的数据,还需要Mock数据请求(get, post, put...)。

Mock请求

  1. 直接在前端代码中Mock请求,这种方式虽然最简便,但是也带来了风险,如果前端人员在真实环境中忘记去除Mock的数据请求,那么就可能造成难以估量的结果;而且由开发环境切换到真实环境中也需要一定的成本;还有,这种方式很难模拟请求的响应速度;

  2. 通过fiddler或者charles等代理的工具,这种方式消除了上述的弊端,但是缺点是灵活性较差,当配置项较多的时候,维护成本也会随之上升巨大;

  3. 使用MockServer来Mock请求(比如:json-serverpuerpuer-mock),缺点是配置复杂,有一定的学习成本,但是灵活高效,可以快速地配置多种请求,比如搜索、过滤、分页、排序等。

技术栈选择

综合考虑,我准备使用mock.js+json-server来打造运营后台的mock-server.

二、Mock数据及扩展

一直在思考mock数据选择faker.js还是Mock.js, 最后综合考虑,觉得Mock.js有良好的文档和可扩展性,而且还是阿里的大神些的,所以最终选择了Mock.js。

Mock.js简介

Mock.js的文档在这里:https://github.com/nuysoft/Mock/wiki/Getting-Started
使用起来很简单:

  1. var data = Mock.mock(tpl);

其中tpl就是数据模板,Mock.js最厉害之处就是它的模板配置非常灵活。

数据模板分为三个部分:属性名、生成规则、属性值。
'name|rule': value

生成规则有以下七种格式:

属性值(value)支持占位符,写法:@占位符(参数 [, 参数]),此外,属性值还可以是函数或者正则表达式。

对Mock.js的扩展

Mock.js对于运营后台的Mock数据的需求还是远远不够的。
我对Mock.js做了如下几点扩展:

1. 支持在一定范围内生成随机日期

Mock.js支持生成随机日期,但是却无法指定在一定范围内生成。
扩展:

  1. // 返回date1和date2之间的日期
  2. between: function(date1, date2, format) {
  3. var min = new Date(date1);
  4. var max = new Date(date2);
  5. format = format || 'HH:mm:ss'
  6. var date = new Date(Math.random() * (max.getTime() - min.getTime()) + min.getTime());
  7. return this._formatDate(date, format)
  8. },

使用方法:

  1. "createTime": '@between(2016-06-01, 2016-12-31, yyyy-MM-dd HH:mm:ss)',
  2. "updateTime": '@between(@createTime, 2016-12-31, yyyy-MM-dd HH:mm:ss)',

2. 支持数组的翻转

Mock.js自增('name|+step': value)所生成的数据必须是正序排列的,但是有些数据需要倒序排列。
为了解决这个问题,我引入了第八种生成规则的格式:name|min-max+r,其中,+r表示对该数组进行翻转

扩展:
首先将正则表达式改为如下,使之能够匹配到('+r'),并且将其作为rule.parameters的第五个参数

  1. RE_KEY: /(.+)\|(?:\+(\d+)|([\+\-]?\d+-?[\+\-]?\d*)?(?:\.(\d+-?\d*))?(\+r)?)/,

然后Handler.extend.array中将数组翻转:

  1. if (options.rule.parameters[5] == '+r') {
  2. result = result.reverse();
  3. }

使用方法:

  1. "moduleConf|1-17+r": [{
  2. "weight|+1": 0
  3. }]

这段模板会生成随机生成1-17个数组,每个数组的weight是从x-0倒序排列的。

3. 支持根元素和父元素的占位符

Mock.js的占位符只能找到当前的字段,对于父元素或者根元素的字段却无能为力。
为此,我对现有的占位符进行了修改:@root.value或者@parent.value
其中,root和parent分别表示根级上下文和父级上下文。

扩展:
首先,对Handler.extend.string进行修改:

  1. if (ph.startsWith('@root.')) {
  2. phed = Handler.placeholder(ph.replace('@root.', "@"), options.context.root, options.context.templateCurrentContext, options)
  3. } else if (ph.startsWith('@parent.')) {
  4. phed = Handler.placeholder(ph.replace('@parent.', "@"), options.context.parent, options.context.templateCurrentContext, options)
  5. } else {
  6. phed = Handler.placeholder(ph, options.context.currentContext, options.context.templateCurrentContext, options)
  7. }

options.context中有root这个参数,但是却没有parent这个参数,所以,要从外层将options.context.currentContext传过来

  1. Handler.gen(options.template[i], i, {
  2. path: options.context.path,
  3. templatePath: options.context.templatePath,
  4. currentContext: result,
  5. templateCurrentContext: options.template,
  6. root: options.context.root || result,
  7. templateRoot: options.context.templateRoot || options.template,
  8. parent: options.context.currentContext || result
  9. })

此外,在Handler.extend.object中还需要判断一下,上层是否为数组,如果上层为数组,则取上上层的context

  1. var parentContext;
  2. if (typeof options.context.path[options.context.path.length - 2] == "number") {
  3. parentContext = options.context.parent || result
  4. } else {
  5. parentContext = options.context.currentContext || result
  6. }

使用方式

  1. {
  2. "id": 1,
  3. "moduleConf|1-17": [{
  4. "groupId": '@parent.id',
  5. }],
  6. "moduleConf2|1-17": {
  7. "groupId": '@parent.id',
  8. }
  9. }

在对象和数组的groundId都可以取到父级的id值。

4. 支持数组内百分比加起来为100%

运营后台还有一些很奇葩的要求,比如某个字段下面的所有数组,有一个percent字段,这些字段的总和必须为100。这里我写的比较死。

扩展:
Handler.extend.array中,生成options.rule.count个随机数

  1. //生成总数为100的options.rule.count个随机数
  2. var rndSum = 0;
  3. var rndArr = [];
  4. for (var rndi = 0; rndi < options.rule.count; rndi++) {
  5. var rnd = Math.random() + 0.05
  6. rndArr.push(rnd)
  7. rndSum += rnd
  8. }
  9. rndArr = rndArr.map(function(r) {
  10. return Math.floor(r / rndSum * 100 + 0.5)
  11. })
  12. rndSum = 0;
  13. for (rndi = 1; rndi < options.rule.count; rndi++) {
  14. rndSum += rndArr[rndi];
  15. }
  16. rndArr[0] = 100 - rndSum;

然后将模板中percent字段的value替换掉

  1. //如果模板中有percent,则将percent替换为百分比
  2. for (var key in optTemplate) {
  3. if (key == 'percent') {
  4. optTemplate[key] = rndArr[i]
  5. }
  6. }

使用方法:

  1. "pageConf|1-10": [{
  2. "percent": 10,
  3. }]

在给pageConf随机生成1至10个数组,并且保证这些数组的percent值的和为100。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注