前端mock數(shù)據(jù)

一纵潦、為什么要模擬數(shù)據(jù)币他?

項(xiàng)目開發(fā)中尔当,前端工程師需要依賴后端工程師的數(shù)據(jù)接口以及后端聯(lián)調(diào)環(huán)境莲祸。但是其實(shí)我們也可以根據(jù)后端接口文檔在接口沒有開發(fā)完成之前自己mock數(shù)據(jù)進(jìn)行調(diào)試蹂安,讓接口消費(fèi)者脫離接口生產(chǎn)者進(jìn)行開發(fā)椭迎。

二、Mock數(shù)據(jù)常見的解決方案有什么田盈?

在 server 端 mock

在 client 端 mock

  1. 在代碼層硬編碼

    在負(fù)責(zé)請(qǐng)求接口的函數(shù)里畜号,直接定義一個(gè)數(shù)據(jù)變量,該變量保存了回返的 Mock數(shù)據(jù)允瞧。

    這種Mock方法操作比較簡(jiǎn)單简软,但缺點(diǎn)也很明顯,就是Mock 更改了代碼邏輯述暂,和代碼耦合性太強(qiáng)痹升。而且并不能模擬真實(shí)的網(wǎng)絡(luò)請(qǐng)求的過程。


  2. 在前端JS中攔截

    典型的解決方案就是Mock.js畦韭,通過在業(yè)務(wù)代碼前掛載該JS文件疼蛾,攔截Ajax請(qǐng)求。這種Mock方式相較于硬編碼艺配,雖然實(shí)現(xiàn)了Mock 與代碼的部分解耦察郁,但無法完全和業(yè)務(wù)代碼解耦(因?yàn)楸仨殥燧d一個(gè)JS并進(jìn)行 Mock配置)。

    雖然提供了大量的Mock API转唉,但是也仍然無法發(fā)出真實(shí)的網(wǎng)絡(luò)請(qǐng)求皮钠,模擬真實(shí)度不夠。另外這種方式還存在一些不足赠法,因?yàn)槭菍?duì)XHR對(duì)象的改寫麦轰,有些情況下兼容性并不好,比如IE8等低版本瀏覽器砖织,還有較新的Fetch API也攔截不到款侵。


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

    Fiddler和Charles可以對(duì)網(wǎng)絡(luò)請(qǐng)求進(jìn)行攔截將其替換為我們需要的Mock 數(shù)據(jù)镶苞,這也不失為一種Mock方式喳坠。其優(yōu)點(diǎn)主要是真實(shí)性強(qiáng),但這種方式操作步驟比較繁瑣茂蚓,不方便統(tǒng)一配置壕鹉,Mock成本較高剃幌。


  4. Mock-Server

    最合適的方案無外乎搭建獨(dú)立的 Mock-Server,開發(fā)的前期階段晾浴,所有的接口都會(huì)指向該 Mock-Server负乡。因?yàn)榭赡艽嬖诳缬虻那闆r,所以一般都需要在開發(fā)環(huán)境搭配一套接口代理做搭配脊凰。這種方案對(duì)業(yè)務(wù)代碼完全不具有侵入性抖棘,并且通用性強(qiáng)。

    缺點(diǎn)也很明顯狸涌,成本高(還需要另起一個(gè)Mock-Server服務(wù)切省,并對(duì)其進(jìn)行管理)。


  5. RAP

    ......

三帕胆、Mock數(shù)據(jù)實(shí)例

1. MockJS

數(shù)據(jù)生成器有很多朝捆,比較出名的有faker、chance懒豹、mockjs等芙盘,其中最為強(qiáng)大的非faker莫屬,不但擁有幾乎全部常用的數(shù)據(jù)格式脸秽,而且還有中英德法等多種語言的數(shù)據(jù)儒老。但是在實(shí)際測(cè)試中發(fā)現(xiàn),faker對(duì)中文數(shù)據(jù)的支持還是以西方文字為基礎(chǔ)记餐,并不能很好的模擬中文驮樊。并且mockjs使用了位于國(guó)內(nèi)的隨機(jī)圖片提供商。

原理:Mock.js 通過覆蓋和模擬原生 XMLHttpRequest 的行為來攔截 Ajax 請(qǐng)求“轉(zhuǎn)發(fā)”到本地文件剥扣,所謂轉(zhuǎn)發(fā)巩剖,其實(shí)就是讀取本地 mock文件,并以json或者script等格式返回給瀏覽器(還需要補(bǔ)充)钠怯。

mockjs能做的事情是攔截Ajax請(qǐng)求佳魔,可以返回各種隨機(jī)的數(shù)據(jù)。

Webpack dev.config.js:

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

DefinePlugin插件允許創(chuàng)建一個(gè)在編譯時(shí)可以配置的全局變量晦炊,
使用功能標(biāo)記來「啟用/禁用」「生產(chǎn)/開發(fā)」構(gòu)建中的功能鞠鲜。

入口文件:

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

Project mock/mock.js:

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

Mock.js的語法規(guī)范:

根據(jù)數(shù)據(jù)模板生成模擬數(shù)據(jù)

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

  • 數(shù)據(jù)模板定義規(guī)范

    數(shù)據(jù)模板中的每個(gè)屬性由 3 部分構(gòu)成:屬性名、生成規(guī)則断国、屬性值

    '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

  • 數(shù)據(jù)占位符定義規(guī)范 @

示例:

'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': [
        '已注冊(cè)',
        '已開戶',
        '已入金',
        '已注銷'
    ],
    'guid': '@guid',
    'first': '@cfirst()',
    'last': '@clast()',
    'name': '@cname',
    // 隨機(jī)生成一個(gè)18位身份證
    'id': '@id',
    'title': '@ctitle(3,10)',
    'paragraph': '@cparagraph(2,5)',
    'image': "@image('200x100', '#4A7BF7', 'img', 'png', 'Tiger')",
    // 隨機(jī)生成一個(gè)6位的郵編
    'zip': '@zip()',
    // 隨機(jī)生成一個(gè)中國(guó)大區(qū)
    'region': '@region()',
    // 隨機(jī)生成一個(gè)(中國(guó))氏湍贰(或直轄市、自治區(qū)稳衬、特別行政區(qū)
    'province': '@province()',
    // 隨機(jī)生成一個(gè)(中國(guó))市霞捡,prefix指示是否生成所屬的省
    'city': '@city(true)',
    // 隨機(jī)生成一個(gè)(中國(guó))縣,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',
    // 隨機(jī)生成一個(gè)域名
    'domain': '@domain()',
    // 隨機(jī)生成一個(gè)頂級(jí)域名
    'tld': '@tld()',
    // 指定郵件的地址域名
    'email': '@email("gmail.com")',
    'color': '@color()',
    'ip': '@ip',
    'regexp': /[a-z][A-Z][0-9]/,
}]

我們希望mock該有的能力:

  • 與線上環(huán)境一致的接口地址碧信,每次構(gòu)建前端代碼時(shí)不需要修改調(diào)用接口的代碼

  • 所改即所得赊琳,具有熱更新的能力,每次增加/修改mock 接口時(shí)不需要重啟mock服務(wù)砰碴,更不用重啟前端構(gòu)建服務(wù)

  • 能配合構(gòu)建工具

  • mock數(shù)據(jù)可以由工具生成不需要自己手動(dòng)寫

  • 能模擬POST躏筏、GET等請(qǐng)求

  • 基于數(shù)據(jù)模板生成數(shù)據(jù)

所以我們有了第二種方案:

2. json-server + mockjs + nodemon

json-server主要是搭建一臺(tái)json服務(wù)器,支持CORS和JSONP跨域請(qǐng)求呈枉。支持 GET趁尼、POST、PUT猖辫、PATCH和DELETE方法酥泞,更提供了一系列的查詢方法,如 limit住册、order等婶博。

CLI usage

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

選項(xiàng):
  --config, -c               指定config文件 [默認(rèn)值: "json-server.json"]
  --port, -p                 設(shè)置端口號(hào) [默認(rèn)值: 3000]
  --host, -H                 設(shè)置主機(jī) [默認(rèn)值: "0.0.0.0"]
  --watch, -w                監(jiān)控文件 [布爾]
  --routes, -r               指定路由文件
  --middlewares, -m          ---- [數(shù)組]
  --static, -s               設(shè)置靜態(tài)文件
  --read-only, --ro          只允許GET請(qǐng)求 [布爾]
  --no-cors, --nc            禁止跨域資源共享 [布爾]
  --no-gzip, --ng            禁止GZIP [布爾]
  --snapshots, -S            設(shè)置快照目錄 [默認(rèn)值: "."]
  --delay, -d                設(shè)置反饋延時(shí)(ms)
  --id, -i                   設(shè)置數(shù)據(jù)的id屬性(e.g. _id) [默認(rèn)值: "id"]
  --foreignKeySuffix, --fks  Set foreign key suffix (e.g. _id as in post_id)
  --quiet, -q                不輸出日志信息 [布爾]
  --help, -h                 顯示幫助信息 [布爾]
  --version, -v              顯示版本號(hào) [布爾]
json-server   // json-server服務(wù)
nodemon       // 修改配置無需重啟服務(wù)
mockjs        // 批量生成數(shù)據(jù)

具體的實(shí)現(xiàn)方案:

package.json

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

目錄結(jié)構(gòu)

|--mock
    |--config.js  配置文件
    |--db.js      數(shù)據(jù)文件
    |--routes.js  路由映射
    |--server.js  服務(wù)文件

config.js -- 配置端口等

module.exports = {
  SERVER:"127.0.0.1",  
  //定義端口號(hào)
  PORT: 3003,
  //定義數(shù)據(jù)文件
  DB_FILE:"./db.js"
};

db.js -- 批量生產(chǎn)數(shù)據(jù)文件

let Mock  = require('mockjs');
let Random = Mock.Random;
module.exports = function() {
  var data = { 
      <!--實(shí)際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));
// 設(shè)置增加一個(gè)響應(yīng)頭信息“從server到前端”
server.use((req, res, next) => {
 res.header('X-Hello', 'World');
 next();
})
// 數(shù)據(jù)發(fā)送到前端之前包一層
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"
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市荧飞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌名党,老刑警劉巖叹阔,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異传睹,居然都是意外死亡耳幢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門欧啤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睛藻,“玉大人,你說我怎么就攤上這事邢隧〉暧。” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵倒慧,是天一觀的道長(zhǎng)按摘。 經(jīng)常有香客問我,道長(zhǎng)纫谅,這世上最難降的妖魔是什么炫贤? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮付秕,結(jié)果婚禮上兰珍,老公的妹妹穿的比我還像新娘。我一直安慰自己询吴,他們只是感情好掠河,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布励幼。 她就那樣靜靜地躺著,像睡著了一般口柳。 火紅的嫁衣襯著肌膚如雪苹粟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天跃闹,我揣著相機(jī)與錄音嵌削,去河邊找鬼。 笑死望艺,一個(gè)胖子當(dāng)著我的面吹牛苛秕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播找默,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼艇劫,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了惩激?” 一聲冷哼從身側(cè)響起店煞,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎风钻,沒想到半個(gè)月后顷蟀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骡技,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年鸣个,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片布朦。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡囤萤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出是趴,到底是詐尸還是另有隱情涛舍,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布右遭,位于F島的核電站做盅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏窘哈。R本人自食惡果不足惜吹榴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望滚婉。 院中可真熱鬧图筹,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓜晤,卻和暖如春锥余,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痢掠。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工驱犹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人足画。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓雄驹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親淹辞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子医舆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容