詳解如何優(yōu)雅在webpack項目實現(xiàn)mock服務(wù)器
為什么需要 mock
至于平時開發(fā)為什么需要 mock 數(shù)據(jù),應(yīng)該大多數(shù)的同學(xué)都非常清楚了;如果前后端同步開發(fā)的話,少不了這一步,在需求評審,技術(shù)評估等流程通過后,前后端就會約定接口 api 的字段(但是在部分公司可能會少了這一步),確認(rèn)接口 api 字段約定之后,前端就可以通過 mock server 去 mock 數(shù)據(jù)進行開發(fā)了,不需要等后端開發(fā)完 api 接口再去對接,但是有些同學(xué)在開發(fā)的過程中經(jīng)常已經(jīng)把 ui 弄好了,就在苦苦等后端大哥的接口...白白浪費了不必要的時間,如果可以自己 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)點:簡單。
缺點:第一,需要侵入邏輯代碼里面;第二,沒辦法真正的模擬 ajax 請求,因為這種 mock 是不會發(fā)起 http 請求的;第三,不能獲取傳遞的參數(shù)去做對應(yīng)的事情;第四,不能修改 http 的狀態(tài);第五,打包項目的時候還會被打包進 bundle。
結(jié)論:缺點明顯大于優(yōu)點。
可視化的 mock
市面上有很多可視化的 mock api 方案,比如 apiFox。
優(yōu)點:ApiFox 集成了 Postman + Swagger + Mock + JMeter,是一款做得比較的好的可視化 mock 解決方案。
缺點:如果只有前端團隊在單獨使用,就有點大材小用沒必要,如果是前后端測試都同時在使用的話,那就是一個不錯的選擇。
結(jié)論:ApiFox 更像一個團隊協(xié)作的 mock 工具。
在 webpack 實現(xiàn) mock server 需要的知識
如果要自己在 webpack 項目的搭建一個定制化的 mock server 需要如下的知識點。
- 一點點的 webpack 知識
- 一點點的 node 知識
很簡單的啦!
實現(xiàn) webpack mock server
小試牛刀
在 webpack 中實現(xiàn)定制化的 mock server ,需要借助 webpack-dev-server,也就是 webpack 配置下 devServer 字段。該字段下提供了一個onBeforeSetupMiddleware
的一個鉤子,回調(diào)參數(shù)里面為我們提供了一個app
參數(shù),參數(shù)是一個 node 的服務(wù)。
既然知道了app
是一個 node 服務(wù),那讓我們小試牛刀一下(很快不疼,一下就過去了 ??)。
// webpack.config.js module.exports = { devServer: { onBeforeSetupMiddleware:(server){ server.app.get('/api/list',(req,res,next){ res.json({ code:1, data:{ name:"孤獵" } }) }) } }, }; // 隨便一個js定義一個ajax請求 let getData = ()=>{ fetch('/api/list').then(res=>res.json()).then(res=>{ console.log(res.data) }) }
這里就實現(xiàn)了一個簡單的 mock server 的了,是不是很簡單。
但是這是有缺點的,總不能沒定義一個 api 都在 onBeforeSetupMiddleware 的 server.app.xxx 吧,這得多麻煩,讓我們稍微修改一下代碼,大概用個小 50 行代碼就好。
大刀闊斧
確認(rèn)需要實現(xiàn)一下怎么的 mock server:
- 只讀取跟目錄的 mock 文件夾下的 js 文件
- js 文件 mock 數(shù)據(jù)需要通過
module.exports={}
導(dǎo)出 - mock server 可以修改各種狀態(tài),支持 GET,POST 等請求
- 支持延時
- 期望的使用方式:
module.exports = {"GET /api/list":(req,res)=>{},"POST /api/list":{},"GET /api/list 3000":(req,res)=>{} },
- 可以是函數(shù)也可以是 json,3000 是延時時間,請求 api 分三段
請求方法 路徑 延時
。
首先,先把剛才的 get 請求改成 use,讓所以的請求都打進來,打進來后不管三七二十一先調(diào) next;然后加個判斷,只處理包含 api/mock 的請求,其他的讓它改干嘛就干嘛。
具體的實現(xiàn)就直接貼代碼了,具體的看注釋就好,在注釋里詳細(xì)解釋。
具體實現(xiàn)就抽離到一個單獨的 js 文件實現(xiàn),具體哪個文件看個人喜歡了。
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)建一個根目錄創(chuàng)建一個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ù)是一個延時函數(shù) let delayFn = (sleep) => { return new Promise((resolve) => { setTimeout(() => { resolve(); }, sleep); }); }; // 最后返回一個函數(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 有延時存在 if (sleep && sleep > 0) { await delayFn(sleep); } // 如果mock api 的值是一個函數(shù) if (isFuntion) { callback(req, res); } else { // 如果mock api 的值是一個json res.json({ ...callback, }); } next(); }; };
在 webpack.config.js 引入剛剛實現(xiàn)的方法
module.exports = { devServer: { onBeforeSetupMiddleware:(server){ server.app.use('*',mockServer()) } }, };
使用
在項目根目錄定義一個 mock 目錄,隨便創(chuàng)一個 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ù)不會被打包進 bundle,也沒有侵入到邏輯代碼里面。
如果各位同學(xué)對 vite 項目怎么實現(xiàn) mock server 感興趣的話,可以留言或者私信,需求多的話,會出一篇在 vite 項目的 mock server。
以上就是詳解如何優(yōu)雅在webpack項目實現(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ū)別,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01