mockjs使用心得

在前后端分离的开发过程中,难免会遇到前端等待后台接口数据的场景。之前的做法是临时写一堆测试数据,然后等待后台开发完毕再对接接口,同时删除之前的测试数据。前段时间跟赛文谈到这里,提到了他们使用的mockjs,这是一个专门用于自动生成测试数据的框架,之前略有耳闻,索性也尝试在vue-cli的项目中集成了mockjs,开发体验确实不错。下面是整理的一点心得。

<!--more-->

参考:

1. 文档整理

mockjs在前后端分离中,大概的使用流程就是:

  • 跟后台约定字段及数据类型,
  • 然后通过mockjs相关接口生成模拟数据
  • 接口开发完毕,完成对接,移除模拟数据

学习mockjs,可以分成两部分:

  • 数据模板语法规则
  • 相关API

由于文档里面已经讲的很详细了,这里就不搬过来了,只是简单记录一下需要注意的地方。

1.1. 语法

mockjs提供了一套特定的语法用于编写数据模板,指定模拟数据的类型和格式,参考文档

数据模板中的每个属性由 3 部分构成:属性名、生成规则、属性值: 'name|rule': value

其中,

  • name值就是我们与后台约定的字段名,
  • value的数据类型和初始值决定了生成规则的具体含义
  • rule生成规则是用来确定字段的生成,这是可选的,共有7种规则
    • min-max
    • count
    • min-max.dmin-dmax
    • min-max.dcount
    • count.dmin-dmax
    • count.dcount,
    • +step,

这里的介绍有点模糊,value的类型必定在JavaScript的7种类型String,Number,Boolean,Object,Array,Function,RegExp中的某一种,value的类型不同,对应的生成规则也不同,举个例子

{
    'id|1-3':10,
    'name|1-3':'abc',
}

这里的规则1-3对应min-max 第一个id|1-3表示生成id字段,其而对应的value10Number,只是用来帮助规则确定实际的含义,这里是:id值位于1~3的范围内。 第二个name|1-3表示生成name字段,其value值类型为String,表示生成的数据由该字符串重复1~3次而成的。

理解“规则的含义由对应的值确定”这一点之后,mockjs的语法规则基本上就掌握了。至于各个数据类型对应的各个规则的含义,多多练习即可。

语法部分需要注意的就是当value值为RegExp类型时,甚至可以根据该正则表达式,反向生成可以匹配它的字符串。这是一个非常强大的功能,这样我们可以用来生成自定义格式的字符串

1.2. mock

通过Mock.mock方法可以快速模拟数据,参考文档

var Mock = require("mockjs");
var data = Mock.mock({
    'list|1-10': [{
        'id|+1':1
    }],
});

mock方法有多个重载,我用的最多的就是拦截指定url的ajax请求,

Mock.mock( rurl?, rtype?, template|function( options ) )

参数含义为:

  • rurl,表示需要拦截的 URL,可以是 URL 字符串或 URL 正则
  • rtype,表示需要拦截的 Ajax 请求类型。例如 GET、POST、PUT、DELETE 等
  • template,表示数据模板,可以是对象或字符串,由前面的语法规则组成
  • function(options),表示用于生成响应数据的函数。
    • options,本次请求的 Ajax 选项集

其实现原理是:

从 1.0 开始,Mock.js 通过覆盖和模拟原生 XMLHttpRequest 的行为来拦截 Ajax 请求

1.3. Random

Mock.Random 是一个工具类,用于生成各种随机数据。

这个函数包含了一系列指令函数,用于生成常见的字段,比如image,color,text等,

var Random = Mock.Random
console.log(Random.email());

在数据模板中,可以通过占位符的形式,生成对应的模拟数据,非常方便。此外针对文本,还有专门的中文指令(是不是很人性化)。

{
    'user_email': '@email'
}

可以把Random工具类理解为内置的数据模板语法糖,我们可以快速生成模拟数据,而不必编写具体的数据模板。

1.4. 其他

上面的mockRandom是比较常用方法,此外还有其他几个API

  • Mock.setup( settings ),用于配置拦截 Ajax 请求时的行为。目前支持的配置项只有:timeout
  • Mock.valid( template, data ),校验真实数据 data 是否与数据模板 template 匹配,可以用于单元测试
  • Mock.toJSONSchema( template ),把 Mock.js 风格的数据模板 template 转换成 JSON Schema,这个我暂时还没有研究过

2. 集成到vue-cli

2.1. 基本方案

我的vue-cli项目的src目录层次大概如下所示


|- api             请求接口
    |- _axios.js   请求配置及相关拦截器
    |- _mock.js    模拟对应请求url的返回数据
    |- test.js     业务接口文件 
    |- ...
|- assets          静态资源
|- components      公共组件
|- filters         全局过滤器,
|- pages           页面模块
    |- p_1       
    |- p_2     
    |- ...
|- router          页面路由配置
|- store           vuex状态管理
|- test            测试文件
|- App.vue         
|- main.js         入口文件

mockjs可以拦截对应的ajax请求,然后返回对应的数据,比如test.js文件中包含如下接口

// test.js
import axios from "axios"

export const test = (params)=>{
    return axios.post(`/api/test`, params).then(res=>res.data);
};

我们只需要在_mock.js中拦截这个请求的路径api/test,根据跟后台预先预定的字段,生成模拟数据

// mock.js
import Mock from "mockjs"

// 拦截对应的url
Mock.mock(/\/api\/test/, 'get', {
    'list|10': [{
        'id|+1': 1,
        'comment_id': 0,
        'uid': '@id',
        'created_time': '@datetime',
        'content': '@csentence',
    }],
    'total': 56
});

同时记得在入口文件main.js引入

// main.js
import "@/api/_mock"
import "@/api/_axios"

可以看见,我们可以在_mock.js中拦截任何我们需要的接口,然后生成一大堆测试数据,在对接接口时,只需要删除对应的请求拦截即可。

如果模拟接口数量较多,我们可以考虑将其拆分成多个文件然后按需引入。不过当后台接口开发完毕时,之前的拦截请求也没有必要了。

上面只是简单地通过将_mock.js是否引入main.js来区分开发环境和接口对接正式环境,这对于开发者来说并不是很友好,需要手动修改引入文件,还需要手动删除多余的mock拦截。

一种更好的选择应当是使用类似于vue-clinpm run devnpm run build这样的指令来切换开发环境。

2.2. 模块化

vue-cli的目录/build/dev-server.js中我们可以发现,开发环境的服务器是使用express搭建的。

因此我们可以直接设置对应的路由请求并返回模拟数据,这样我们就不需要手动地在main.js中引入和移除_mock.js文件。

// express模拟请求
app.get('/api/data', (req, res)=>{
  res.send(Mock.mock({
    "list|5": [{
      "id|+1":1,
      "name": "@word"
    }]
  }));
})

这里带来的问题是如果模拟接口过多,在dev-serveer中会包含过多的模拟数据代码,这肯定不是一个明智之举。此外,由于直接调用了Mock.mock(template)的形式,并没有用到其拦截url请求的特性。(如果后台是nodejs的话,可以让后台先使用mockjs直接返回模拟数据,这样就不需要前端处理了~)

我们可以通过express的路由组件,将对应的模拟请求置于单独的文件中(跟前面纯前端处理类似),整个世界就清静了。

新建一个mock文件夹,放置所有的模拟请求

// mock/index.js

var express = require("express");
var router = express.Router();
var Mock = require("mockjs");

router.use("/data", function(req, res, next) {
    res.send(Mock.mock({
        "list|5": [{
            "id|+1": 1,
            "name": "@word",
            "title": "@ctitle"
        }]
    }));
});

module.exports = router;

dev-server中,使用该路由组件即可

if(config.dev.isMock){
    app.use('/api', require('../mock/'))
}

上面的config.dev.isMock是在配置文件/config/index.js中额外增加的配置变量,来决定是否使用模拟路由组件,从而在正式接口和模拟接口之间来回切换

3. 最后

上面简单整理了mockjs的使用,以及集成到vue-cli的方法。当然,mockjs不仅限于vue-cli的使用中,在其他很多需要模拟数据的地方 ,都可以使用mockjs。(我对于里面的反向正则匹配很感兴趣,哈哈)