前端mock数据

一、为什么要模拟数据?

项目开发中,前端工程师需要依赖后端工程师的数据接口以及后端联调环境。但是其实我们也可以根据后端接口文档在接口没有开发完成之前自己mock数据进行调试,让接口消费者脱离接口生产者进行开发。

二、Mock数据常见的解决方案有什么?

在 server 端 mock

在 client 端 mock

  1. 在代码层硬编码

    在负责请求接口的函数里,直接定义一个数据变量,该变量保存了回返的 Mock数据。

    这种Mock方法操作比较简单,但缺点也很明显,就是Mock 更改了代码逻辑,和代码耦合性太强。而且并不能模拟真实的网络请求的过程。


  2. 在前端JS中拦截

    典型的解决方案就是Mock.js,通过在业务代码前挂载该JS文件,拦截Ajax请求。这种Mock方式相较于硬编码,虽然实现了Mock 与代码的部分解耦,但无法完全和业务代码解耦(因为必须挂载一个JS并进行 Mock配置)。

    虽然提供了大量的Mock API,但是也仍然无法发出真实的网络请求,模拟真实度不够。另外这种方式还存在一些不足,因为是对XHR对象的改写,有些情况下兼容性并不好,比如IE8等低版本浏览器,还有较新的Fetch API也拦截不到。


  3. 代理软件(Fiddler、Charles抓包

    Fiddler和Charles可以对网络请求进行拦截将其替换为我们需要的Mock 数据,这也不失为一种Mock方式。其优点主要是真实性强,但这种方式操作步骤比较繁琐,不方便统一配置,Mock成本较高。


  4. Mock-Server

    最合适的方案无外乎搭建独立的 Mock-Server,开发的前期阶段,所有的接口都会指向该 Mock-Server。因为可能存在跨域的情况,所以一般都需要在开发环境搭配一套接口代理做搭配。这种方案对业务代码完全不具有侵入性,并且通用性强。

    缺点也很明显,成本高(还需要另起一个Mock-Server服务,并对其进行管理)。


  5. RAP

    ......

三、Mock数据实例

1. MockJS

数据生成器有很多,比较出名的有faker、chance、mockjs等,其中最为强大的非faker莫属,不但拥有几乎全部常用的数据格式,而且还有中英德法等多种语言的数据。但是在实际测试中发现,faker对中文数据的支持还是以西方文字为基础,并不能很好的模拟中文。并且mockjs使用了位于国内的随机图片提供商。

原理:Mock.js 通过覆盖和模拟原生 XMLHttpRequest 的行为来拦截 Ajax 请求“转发”到本地文件,所谓转发,其实就是读取本地 mock文件,并以json或者script等格式返回给浏览器(还需要补充)。

mockjs能做的事情是拦截Ajax请求,可以返回各种随机的数据。

Webpack dev.config.js:

plugins:[
    new webpack.DefinePlugin({
        MOCK: true
    })
]

DefinePlugin插件允许创建一个在编译时可以配置的全局变量,
使用功能标记来「启用/禁用」「生产/开发」构建中的功能。

入口文件:

if (MOCK) {
    require('mock/mock');
}

Project mock/mock.js:

import Mock from 'mockjs';
Mock.mock('/api/user', {
    'name': '@cname',
    'intro': '@word(20)'
});

Mock.js的语法规范:

根据数据模板生成模拟数据

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

  • 数据模板定义规范

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

    'name|rule': value

    'name|min-max': value

    'name|count': value

    'name|min-max.dmin-dmax': value

    'name|min-max.dcount': value

    'name|count.dmin-dmax': value

    'name|count.dcount': value

    'name|+step': value

  • 数据占位符定义规范 @

示例:

'list|1-10': [{
    'order_id|+1': 1,
    'user_id|100-200': 1,
    'is_deposit|1': true,
    'city|2-4': {
        "110000": "北京市",
        "120000": "天津市",
        "130000": "河北省",
        "140000": "山西省"
    },
    'status|1': [
        '已注册',
        '已开户',
        '已入金',
        '已注销'
    ],
    'guid': '@guid',
    'first': '@cfirst()',
    'last': '@clast()',
    'name': '@cname',
    // 随机生成一个18位身份证
    'id': '@id',
    'title': '@ctitle(3,10)',
    'paragraph': '@cparagraph(2,5)',
    'image': "@image('200x100', '#4A7BF7', 'img', 'png', 'Tiger')",
    // 随机生成一个6位的邮编
    'zip': '@zip()',
    // 随机生成一个中国大区
    'region': '@region()',
    // 随机生成一个(中国)省(或直辖市、自治区、特别行政区
    'province': '@province()',
    // 随机生成一个(中国)市,prefix指示是否生成所属的省
    'city': '@city(true)',
    // 随机生成一个(中国)县,prefix指示是否生成所属的省、市
    'address': '@county(true)',
    'date': '@date("yyyy-MM-dd")',
    'datetime': '@datetime("yyyy-MM-dd HH:mm:ss")',
    'time': '@time("HH:mm:ss")',
    'sentence': '@csentence(2, 5)',
    'url': '@url',
    // 随机生成一个域名
    'domain': '@domain()',
    // 随机生成一个顶级域名
    'tld': '@tld()',
    // 指定邮件的地址域名
    'email': '@email("gmail.com")',
    'color': '@color()',
    'ip': '@ip',
    'regexp': /[a-z][A-Z][0-9]/,
}]

我们希望mock该有的能力:

  • 与线上环境一致的接口地址,每次构建前端代码时不需要修改调用接口的代码

  • 所改即所得,具有热更新的能力,每次增加/修改mock 接口时不需要重启mock服务,更不用重启前端构建服务

  • 能配合构建工具

  • mock数据可以由工具生成不需要自己手动写

  • 能模拟POST、GET等请求

  • 基于数据模板生成数据

所以我们有了第二种方案:

2. json-server + mockjs + nodemon

json-server主要是搭建一台json服务器,支持CORS和JSONP跨域请求。支持 GET、POST、PUT、PATCH和DELETE方法,更提供了一系列的查询方法,如 limit、order等。

CLI usage

➜ json-server -h
json-server [options] <source>

选项:
  --config, -c               指定config文件 [默认值: "json-server.json"]
  --port, -p                 设置端口号 [默认值: 3000]
  --host, -H                 设置主机 [默认值: "0.0.0.0"]
  --watch, -w                监控文件 [布尔]
  --routes, -r               指定路由文件
  --middlewares, -m          ---- [数组]
  --static, -s               设置静态文件
  --read-only, --ro          只允许GET请求 [布尔]
  --no-cors, --nc            禁止跨域资源共享 [布尔]
  --no-gzip, --ng            禁止GZIP [布尔]
  --snapshots, -S            设置快照目录 [默认值: "."]
  --delay, -d                设置反馈延时(ms)
  --id, -i                   设置数据的id属性(e.g. _id) [默认值: "id"]
  --foreignKeySuffix, --fks  Set foreign key suffix (e.g. _id as in post_id)
  --quiet, -q                不输出日志信息 [布尔]
  --help, -h                 显示帮助信息 [布尔]
  --version, -v              显示版本号 [布尔]
json-server   // json-server服务
nodemon       // 修改配置无需重启服务
mockjs        // 批量生成数据

具体的实现方案:

package.json

"scripts": {
    ...
    "mockdev": "nodemon mock/server.js & npm start"
  }

目录结构

|--mock
    |--config.js  配置文件
    |--db.js      数据文件
    |--routes.js  路由映射
    |--server.js  服务文件

config.js -- 配置端口等

module.exports = {
  SERVER:"127.0.0.1",  
  //定义端口号
  PORT: 3003,
  //定义数据文件
  DB_FILE:"./db.js"
};

db.js -- 批量生产数据文件

let Mock  = require('mockjs');
let Random = Mock.Random;
module.exports = function() {
  var data = { 
      <!--实际mock的接口-->
  };
  return data;
}

server.js

const config = require('./config');
const jsonServer = require('json-server');
const rules = require('./routes');
const dbfile = require(config.DB_FILE);

const ip = config.SERVER;
const port = config.PORT;
const db_file = config.DB_FILE;
// Express server
const server = jsonServer.create();
// JSON Server router
const router = jsonServer.router(dbfile());
// 中间件
const middlewares = jsonServer.defaults();

server.use(jsonServer.bodyParser);
server.use(middlewares);
// 重写路由
server.use(jsonServer.rewriter(rules));
// 设置增加一个响应头信息“从server到前端”
server.use((req, res, next) => {
 res.header('X-Hello', 'World');
 next();
})
// 数据发送到前端之前包一层
router.render = (req, res) => {
    res.jsonp({
        code: 0,
        body: res.locals.data
    })
}

server.use(jsonServer.rewriter(rules));
server.use(router);

server.listen({
    host: ip,
    port: port,
}, function() {
    console.log(JSON.stringify(jsonServer));
    console.log(`JSON Server is running in http://${ip}:${port}`);
});

routes.js -- 路由映射

module.exports= {
    "/api/": "/",
    "/:id": "/news/:id",
    "/news/:id/show": "/news/:id",
    "/topics/:id/show": "/news/:id"
}

webpack.dev.cong.js -- 代理

devServer: {
    ...
    proxy: {
        "/api/*": "http://localhost:3003"
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容