詳解如何優(yōu)雅在webpack項(xiàng)目實(shí)現(xiàn)mock服務(wù)器
為什么需要 mock
至于平時(shí)開發(fā)為什么需要 mock 數(shù)據(jù),應(yīng)該大多數(shù)的同學(xué)都非常清楚了;如果前后端同步開發(fā)的話,少不了這一步,在需求評(píng)審,技術(shù)評(píng)估等流程通過后,前后端就會(huì)約定接口 api 的字段(但是在部分公司可能會(huì)少了這一步),確認(rèn)接口 api 字段約定之后,前端就可以通過 mock server 去 mock 數(shù)據(jù)進(jìn)行開發(fā)了,不需要等后端開發(fā)完 api 接口再去對接,但是有些同學(xué)在開發(fā)的過程中經(jīng)常已經(jīng)把 ui 弄好了,就在苦苦等后端大哥的接口...白白浪費(fèi)了不必要的時(shí)間,如果可以自己 mock 數(shù)據(jù)開發(fā),那等后端接口都好了只需要把域名或者接口前綴換一下再聯(lián)調(diào)一下就萬事大吉了。
mock 數(shù)據(jù)的方式
json schema
有些同學(xué)喜歡在代碼里面用 json schema 的形式去 mock 數(shù)據(jù),比如:
export function xhr(params = {}) {
if (process.env.NODE_ENV === "development" && useMock) {
return delay(2000).then(() => Promise.resolve(require("./mock/list.json")));
}
return request({
url: "xxxxx",
method: "POST",
});
}
function delay(ms = 2000) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
}
優(yōu)點(diǎn):簡單。
缺點(diǎn):第一,需要侵入邏輯代碼里面;第二,沒辦法真正的模擬 ajax 請求,因?yàn)檫@種 mock 是不會(huì)發(fā)起 http 請求的;第三,不能獲取傳遞的參數(shù)去做對應(yīng)的事情;第四,不能修改 http 的狀態(tài);第五,打包項(xiàng)目的時(shí)候還會(huì)被打包進(jìn) bundle。
結(jié)論:缺點(diǎn)明顯大于優(yōu)點(diǎn)。
可視化的 mock
市面上有很多可視化的 mock api 方案,比如 apiFox。
優(yōu)點(diǎn):ApiFox 集成了 Postman + Swagger + Mock + JMeter,是一款做得比較的好的可視化 mock 解決方案。
缺點(diǎn):如果只有前端團(tuán)隊(duì)在單獨(dú)使用,就有點(diǎn)大材小用沒必要,如果是前后端測試都同時(shí)在使用的話,那就是一個(gè)不錯(cuò)的選擇。
結(jié)論:ApiFox 更像一個(gè)團(tuán)隊(duì)協(xié)作的 mock 工具。
在 webpack 實(shí)現(xiàn) mock server 需要的知識(shí)
如果要自己在 webpack 項(xiàng)目的搭建一個(gè)定制化的 mock server 需要如下的知識(shí)點(diǎn)。
- 一點(diǎn)點(diǎn)的 webpack 知識(shí)
- 一點(diǎn)點(diǎn)的 node 知識(shí)
很簡單的啦!
實(shí)現(xiàn) webpack mock server
小試牛刀
在 webpack 中實(shí)現(xiàn)定制化的 mock server ,需要借助 webpack-dev-server,也就是 webpack 配置下 devServer 字段。該字段下提供了一個(gè)onBeforeSetupMiddleware的一個(gè)鉤子,回調(diào)參數(shù)里面為我們提供了一個(gè)app參數(shù),參數(shù)是一個(gè) node 的服務(wù)。
既然知道了app是一個(gè) node 服務(wù),那讓我們小試牛刀一下(很快不疼,一下就過去了 ??)。
// webpack.config.js
module.exports = {
devServer: {
onBeforeSetupMiddleware:(server){
server.app.get('/api/list',(req,res,next){
res.json({
code:1,
data:{
name:"孤獵"
}
})
})
}
},
};
// 隨便一個(gè)js定義一個(gè)ajax請求
let getData = ()=>{
fetch('/api/list').then(res=>res.json()).then(res=>{
console.log(res.data)
})
}
這里就實(shí)現(xiàn)了一個(gè)簡單的 mock server 的了,是不是很簡單。
但是這是有缺點(diǎn)的,總不能沒定義一個(gè) api 都在 onBeforeSetupMiddleware 的 server.app.xxx 吧,這得多麻煩,讓我們稍微修改一下代碼,大概用個(gè)小 50 行代碼就好。
大刀闊斧
確認(rèn)需要實(shí)現(xiàn)一下怎么的 mock server:
- 只讀取跟目錄的 mock 文件夾下的 js 文件
- js 文件 mock 數(shù)據(jù)需要通過
module.exports={}導(dǎo)出 - mock server 可以修改各種狀態(tài),支持 GET,POST 等請求
- 支持延時(shí)
- 期望的使用方式:
module.exports = {"GET /api/list":(req,res)=>{},"POST /api/list":{},"GET /api/list 3000":(req,res)=>{} },
- 可以是函數(shù)也可以是 json,3000 是延時(shí)時(shí)間,請求 api 分三段
請求方法 路徑 延時(shí)。
首先,先把剛才的 get 請求改成 use,讓所以的請求都打進(jìn)來,打進(jìn)來后不管三七二十一先調(diào) next;然后加個(gè)判斷,只處理包含 api/mock 的請求,其他的讓它改干嘛就干嘛。
具體的實(shí)現(xiàn)就直接貼代碼了,具體的看注釋就好,在注釋里詳細(xì)解釋。
具體實(shí)現(xiàn)就抽離到一個(gè)單獨(dú)的 js 文件實(shí)現(xiàn),具體哪個(gè)文件看個(gè)人喜歡了。
const fs = require("fs");
const path = require("path");
module.exports = function () {
// 這里收mock數(shù)據(jù)的根目錄,我們只認(rèn)這目錄下文件
let mockDataPath = path.resolve(__dirname, "../mock/");
// 判斷根目錄是否存在mock目錄
let existsMockDir = fs.existsSync(mockDataPath);
// 獲取mock目錄下的所有文件的mock數(shù)據(jù)
let getMockData = () => {
// 如果mock目錄存在就走if邏輯
if (existsMockDir) {
/**
* 通過readdirSync獲取mock目錄下的所有文件名稱
* 再通過require取出數(shù)據(jù)
*/
let modules = fs.readdirSync(mockDataPath);
return modules.reduce((pre, module) => {
return {
...pre,
...require(path.join(mockDataPath, "./" + module)),
};
}, {});
} else {
console.log("根目錄不存在mock文件夾,請創(chuàng)建一個(gè)根目錄創(chuàng)建一個(gè)mock文件夾");
return {};
}
};
// 該函數(shù)負(fù)責(zé)重新處理請求的路徑
let splitApiPath = (mockData) => {
let data = {};
for (let path in mockData) {
let [method, apiPath, sleep] = path.split(" ");
let newApiPath = method.toLocaleUpperCase() + apiPath;
data[newApiPath] = {
path: newApiPath,
method,
sleep,
callback: mockData[path],
};
}
return data;
};
// 該函數(shù)是一個(gè)延時(shí)函數(shù)
let delayFn = (sleep) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, sleep);
});
};
// 最后返回一個(gè)函數(shù)
return async (req, res, next) => {
let { baseUrl, method } = req;
// 只處理請求路徑包含api的請求
if (baseUrl.indexOf("api") === -1 || !existsMockDir) {
return next();
}
let mockData = splitApiPath(getMockData());
let path = method.toLocaleUpperCase() + baseUrl;
let { sleep, callback } = mockData[path];
let isFuntion = callback.__proto__ === Function.prototype;
// 如果mock api 有延時(shí)存在
if (sleep && sleep > 0) {
await delayFn(sleep);
}
// 如果mock api 的值是一個(gè)函數(shù)
if (isFuntion) {
callback(req, res);
} else {
// 如果mock api 的值是一個(gè)json
res.json({
...callback,
});
}
next();
};
};
在 webpack.config.js 引入剛剛實(shí)現(xiàn)的方法
module.exports = {
devServer: {
onBeforeSetupMiddleware:(server){
server.app.use('*',mockServer())
}
},
};
使用
在項(xiàng)目根目錄定義一個(gè) mock 目錄,隨便創(chuàng)一個(gè) js 文件。
module.exports = {
"GET /api/list": {
code: 0,
data: {
list: [
{
name: "syf",
age: 18,
},
],
},
},
"POST /api/list 3000": (req, res) => {
res.json({
code: 0,
data: {
list: [
{
name: "gulie",
age: 19,
},
],
},
});
},
};
到此為止,期望的定制化的 mock serve 就大功告成了。
后話
如果各位同學(xué)還有更高的追求的話,可以在此基礎(chǔ)上繼續(xù)定制化自己的需求,還有在根目錄的 mock 所有數(shù)據(jù)不會(huì)被打包進(jìn) bundle,也沒有侵入到邏輯代碼里面。
如果各位同學(xué)對 vite 項(xiàng)目怎么實(shí)現(xiàn) mock server 感興趣的話,可以留言或者私信,需求多的話,會(huì)出一篇在 vite 項(xiàng)目的 mock server。
以上就是詳解如何優(yōu)雅在webpack項(xiàng)目實(shí)現(xiàn)mock服務(wù)器的詳細(xì)內(nèi)容,更多關(guān)于webpack mock服務(wù)器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
node全局變量__dirname與__filename的區(qū)別
這篇文章主要介紹了node全局變量__dirname與__filename的區(qū)別,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01
Nodejs中koa2連接mysql的實(shí)現(xiàn)示例
本文主要介紹了Nodejs中koa2連接mysql的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

