首先咱們從使用的角度出發,思考編碼過程webpack
M1. 經過配置文件配置url和responsegit
M2. 自動檢測環境爲開發環境時啓動Mock.jsgithub
M3. mock代碼能直接覆蓋global.fetch方法或者XMLHttpRequest構造函數,實現開發無感知web
M4. mock配置不影響實際的請求,可無縫切換爲實際請求ajax
比較符合咱們使用習慣的,也許是下面這種mock方式,有一個專門的配置文件,管理請求的url和返回值。每一個請求對應輸出數組中的一個對象,對象的rule屬性能夠是一個字符串或者一個正則表達式,用來匹配url,對象的res屬性則是咱們但願的從中請求中拿到的返回的數據 (也許這裏面還應該加個type表示請求的類型,可是我這個是mock的最簡化版,因此就不加了)正則表達式
// api.js module.exports = [ { rule: '/mock', res: { a: 'data', b: [{c: 1}, {d: 1}], }, }, { rule: '/mock2', res: { j: { k: 'XXX' }, }, }, ];
// __DEV__ 多是webpack等配置的全局變量 if (__DEV__) { require ('./ajaxMock.js'); require ('./fetchMock.js'); }
// fetchMock.js window.fetch = function (url) { // 覆蓋默認fetch } // ajaxMock.js class XMLHttpRequest { // ...覆蓋默認XHR } window.XMLHttpRequest = XMLHttpRequest;
mock配置不影響實際的請求,當請求沒有命中mock配置文件中的url時,自動切換爲實際請求,例如json
// fetch window.fetch = (url, cfg) => { if (命中config文件中的url) { // 覆蓋默認fetch } else { return originFetch (url, cfg); } }; // Ajax const RealXHR = window.XMLHttpRequest; class XMLHttpRequest { open (type, url, bool) { if (命中config文件中的url) { // 覆蓋Ajax } else { // 使用系統原有的Ajax this.xhr = new RealXHR (); this.xhr.open (type, url, bool); } } send (args) { if (命中config文件中的url) { // 覆蓋Ajax } else { // 使用系統原有的Ajax this.xhr.send (args); } } } window.XMLHttpRequest = XMLHttpRequest;
直接上代碼api
// 保存系統原生的fetch const originFetch = window.fetch; // 根據fetch的要求返回的response const normalize = resp => { return { ok: true, status: 200, text () { return Promise.resolve (resp); }, json () { return Promise.resolve (resp); }, }; }; // 覆蓋fetch window.fetch = (url, cfg) => { // url所對應的JSON對象 let res; // 表示是否config文件中是否有和url對應的配置 let hit = false; // 遍歷配置文件中輸出的數組,檢測並嘗試獲取匹配url的res對象 fakeApi.forEach (item => { let rule = item.rule; if (typeof rule === 'string') { rule = new RegExp (rule); } if (rule && rule.test (url)) { res = item.res; hit = true; return false; } }); // 若是命中,那麼返回一個Promise,而且傳遞上面和url匹配的JSON對象 if (hit) { return new Promise (resolve => { setTimeout (() => { resolve (normalize (res)); }, 1000); }); } // 若是沒有命中,那麼使用系統原有的fetch的API,實現無縫切換 return originFetch (url, cfg); };
直接上代碼數組
// 保存系統原生的XMLHttpRequest對象 const RealXHR = window.XMLHttpRequest; class XMLHttpRequest { constructor () { this.url = null; this.type = null; this.hit = false; // 真實的xhr this.xhr = null; } open (type, url, bool) { // 遍歷配置文件中輸出的數組,檢測並嘗試獲取匹配url的res對象 fakeApi.forEach (item => { let rule = item.rule; if (typeof rule === 'string') { rule = new RegExp (rule); } if (rule && rule.test (url)) { this.res = item.res; this.hit = true; return false; } }); // 若是沒有命中,那麼使用系統原有的Ajax的API,實現無縫切換 if (!this.hit) { this.xhr = new RealXHR (); this.xhr.open (type, url, bool); } } send (args) { // 若是命中,就覆蓋Ajax的API if (this.hit && this.onreadystatechange) { this.readyState = 4; this.status = 200; this.responseText = JSON.stringify (this.res); this.onreadystatechange (); } else { // 若是沒有命中,那麼使用系統原有的Ajax的API,實現無縫切換 this.xhr.send (args); } } } // 覆蓋 window.XMLHttpRequest = XMLHttpRequest;
配置文件 dom
export default [ { rule: '/mock', res: { a: 'data', b: [{c: 1}, {d: 1}], }, } ];
測試代碼
const xhr = new XMLHttpRequest (); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { console.log (JSON.parse (xhr.responseText)); } }; xhr.open ('GET', '/mock'); xhr.send ();
測試結果
除了上面的功能外,咱們還能作什麼?
加個type類型,區分同一url下的不一樣請求類型,例如get,post
加個布爾值err,表示失敗的請求
上面這兩個功能再作了我以爲就已經很足夠了,固然,若是你還不知足,那你還能夠嘗試:
處理xhr.open的第三個參數:async值,控制同步和異步
處理xhr的progress,load,error,abort等事件監聽
處理fetch返回的response的其餘方法,例如Body.formData()等等
早在以前我就寫過一篇關於mock.js的文章。這個庫目前在github是13k, 固然我以爲這個庫是很強大的,由於它覆蓋了從名字,地名,文章甚至是圖片資源的mock數據,可是在實際使用中卻多少有那麼一點點「雞肋」的感受,爲何我會有這樣一種感受呢
這是由於它有一套本身的獨立的模板語法,以及API,須要你學習和遵循
// 模擬JSON數據 Mock.mock({ "array|1-10": [ "Hello", "Mock.js", "!" ] }) // 模擬大段的文章或句子 Random.paragraph( min?, max? )
固然mock.js有它本身的好處,例如:
當你須要動態地造大數據量的mock數據的時候很方便,例如mock.js的Random.paragraph的API能很方便的幫你造出來
當你有一些特殊的需求點的時候,例如一個長度寬度變化的圖片的時候,mock.js也能夠很強大的勝任Random.image( size?, background?)
造出來的數據看起來「很漂亮很真實」,單純看徹底發現不了是假的數據
但問題在於,我在實際的開發中發現,咱們大多數的數據場景根本就沒這麼複雜
咱們大多數時候須要的僅僅只是:寫一個響應數據的模版,例如一個json文件,而後使得發一個請求過去的時候能在ajax的onreadystatechange或者fetch(url).then中拿到數據就能夠了
若是符合咱們預期的mock的「完美需求」是100%的話
mock.js這個社區應用實現了80%到99%的需求的過程
可是它的使用方式卻額外增長了30% ~ 40%的成本,
由於,咱們大多數時候也許不太須要這麼多的模板和「看起來很漂亮的數據」
這是我寫這個簡易版的mock的實現的緣由
才疏學淺,還多指教,本文完