預期的mock的使用方式
首先我們從使用的角度出發,思考編碼過程
- M1. 通過配置檔案配置url和response
- M2. 自動檢測環境為開發環境時啟動Mock.js
- M3. mock代碼能直接覆寫global.fetch方法或者XMLHttpRequest構造函數,實作開發無感覺
- M4. mock配置不影響實際的請求,可無縫切換為實際請求
M1. 通過配置檔案配置url和response
比較符合我們使用習慣的,也許是下面這種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'
},
},
},
];
M2. 自動檢測環境為開發環境時啟動Mock.js
// __DEV__ 可能是webpack等配置的全局變量
if (__DEV__) {
require ('./ajaxMock.js');
require ('./fetchMock.js');
}
M3. mock代碼能直接覆寫global.fetch方法或者XMLHttpRequest構造函數,實作開發無感覺
// fetchMock.js
window.fetch = function (url) {
// 覆寫預設fetch
}
// ajaxMock.js
class XMLHttpRequest {
// ...覆寫預設XHR
}
window.XMLHttpRequest = XMLHttpRequest;
M4.mock配置不影響實際的請求,可無縫切換為實際請求
mock配置不影響實際的請求,當請求沒有命中mock配置檔案中的url時,自動切換為實際請求,例如
// 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;
模拟fetch
直接上代碼
// 儲存系統原生的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);
};
模拟ajax
// 儲存系統原生的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;
測試
配置檔案
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
早在之前我就寫過一篇關于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%的成本,