花 10 分鐘,插入 100 行代碼,0 額外依賴,讓你項目支持 mock,支持任意 webpack 工程,簡單易用前端
目前已將公司傳統 PC 項目和 React-Native 項目進行了改造,效果不錯,特地分享出來node
寫了一個小 demo github.com/imaoda/gene… 供體驗webpack
常規 mock 的手段有:ios
這些緣由阻礙了咱們 mock 的腳步:git
本方案的優點:github
本質上仍是請求攔截,並不是新花樣,重點在後面的細節的處理技巧,會讓這個 mock 方案變得絲滑易用web
常見的請求好比 axios
、fetch
、wx.request
,並非全部請求都有攔截器方法,何況攔截器也不是萬能的,一般咱們的項目都會進行 請求再封裝,作諸如如下的事情:chrome
好比咱們封裝 fetch,讓請求支持 mockdocker
import mockList from '../../mock'; // 引入寫好的 mock 數據
// 封裝 fetch,若是 url 命中 url 則直接返回 mock 數據,不然走正常請求
export default async function request(url) {
const resArr = mockList.filter(item => item.url === url);
return resArr[0] || (await fetch(url).then(i => i.json()));
}
複製代碼
咱們會將整個 mock 文件夾下文件引入,做爲 mock 數據集,好比,咱們在 mock/index.js
下引入全部其餘文件,併合並導出,以下圖:json
麻煩的事來了,一旦目錄結構發生變化,好比新增,刪除,批量調整,嵌套文件夾等,咱們都須要頻繁的修改 mock/index.js
文件,引入這些數據,併合並,再導出
那麼怎麼才能方便的全量引入呢?
這時,有同窗可能會說:用 fs.readdirSync
讀取整個 mock 文件夾呀!
思路是對的,可是沒法成行;由於畢竟這是前端工程,而非 node 工程,fs.readdirSync
是運行時處理的函數,而前端工程的運行時已經在瀏覽器端了,瀏覽器端何來的 fs
?
咱們能夠利用 require.context 來解決,該 API 既非 commonjs 語法,也不是 ES module 語法,而是由 webpack
提供的
webpack
在編譯時,詞法解析到該 api,會將這段代碼放入 node 運行時去 執行
,並將結果拼接到打包好的 module
中
require.context
進行批量引入前面囉嗦了半天理論,接下來使用 require.context 來引入。該套路比較固定,所以,不用刻意去理解
let routes = [] // 收集全部 mock 數據
// 遍歷目錄下 mock,開啓遞歸遍歷,匹配 (js|ts|jsx|tsx) 結尾的文件
const ctx = require.context('../../mock', true, /(js|ts|jsx|tsx)$/);
// 對命中的文件進行引入,用 cjs 語法引入 esm 導出的,需加 .default
ctx.keys().forEach((file) => {
const content = ctx(file).default;
if (content instanceof Array) {
routes = [...routes, ...content];
} else console.warn(`mock 文件${file}格式不是數組`);
});
// 導出全部
export default routes;
複製代碼
如何開啓、關閉 mock,方式有不少中,整體來講,最佳實踐就是在 package.json 的 script 裏新增一個命令,好比
"script": {
"build": "...",
"dev": "...",
"mock": "xxx dev --mock"
}
複製代碼
經過 webpack 的 definePlugin 爲代碼注入變量,告知是否走的 yarn mock
命令啓動的調試:
// webpack 配置中,plugins 裏增長:
new webpack.DefinePlugin({ __IS_MOCK__, process.argv.includes('--mock') }) // 簡單起見就不用 minimist 解析參數了
複製代碼
此時,業務代碼裏已經注入了常量 __IS_MOCK__
最後,咱們再小改動一下以前封裝的 request
export default async function request(url) {
// 若是未開啓 mock 直接返回
if(!__IS_MOCK__) return await fetch(url).then(i => i.json())
const resArr = mockList.filter(item => item.url === url);
return resArr[0] || (await fetch(url).then(i => i.json()));
}
複製代碼
提交到 git 倉庫上,任意組員拉下來,只需 2 步,開啓 mock
// xx.js 文件
export default [
{ url: '/user/id', data: 10086, code: 0 },
{ url: '/user/name', data: 'wyf', code: 0 },
];
複製代碼
yarn mock
(具體根據你剛纔配置的命令至此,主要內容已經完成,若是還想在項目中繼續優化探索,能夠繼續看下面的部分
ignore 以後,mock 文件夾不會被 git 倉庫收錄,也就是多人合做開發的時候,你們的 mock 文件互不干擾。
我我的理解:
值得注意的是,若是 ignore 掉了 mock 文件夾,require.context
會報錯,此時加上 try catch,webpack 就會在找不到 mock 文件夾時跳過不處理,如:
let routes = [] // 收集全部 mock 數據
try {
const ctx = require.context('../../mock', true, /(js|ts|jsx|tsx)$/);
/** 略 **/
} catch (e) {}
// 導出全部
export default routes;
複製代碼
若是工程 gitignore 了 mock 文件夾,上傳的到 docker 構建的時候,是沒有該文件夾的,所以不會影響到打包的體積
若是沒有 ignore,且不作任何處理,很不幸的是,你的最終 bundle 會帶上這些 mock 數據。解決方案就是在執行 require.context
前加入 if 條件,僅在 mock 打開的時候打包,如:
let routes = [] // 收集全部 mock 數據
try {
if(__IS_MOCK__) {
const ctx = require.context('../../mock', true, /(js|ts|jsx|tsx)$/);
/** 略 **/
}
} catch (e) {}
// 導出全部
export default routes;
複製代碼
本方案是一個很是基礎的工程方案,同時因爲它沒有過多依賴,因此很是靈活,你徹底能夠在攔截階段,對獲取的數據進行處理,好比自定義一些模板語法
本方案的優點,在於靈活,不只僅對 XHR 請求進行攔截,任意環境,好比小程序、fetch 等