0依賴 100行代碼,爲項目添加 mock

寫在前面的話

花 10 分鐘,插入 100 行代碼,0 額外依賴,讓你項目支持 mock,支持任意 webpack 工程,簡單易用前端

目前已將公司傳統 PC 項目和 React-Native 項目進行了改造,效果不錯,特地分享出來node

寫了一個小 demo github.com/imaoda/gene… 供體驗webpack

本方案的優點

常規 mock 的手段有:ios

  • 利用 webpack 的 devSever
  • 啓動 node 層的服務
  • 利用 charles 代理
  • 利用 chrome 插件去代理到 mock 服務
  • 攔截請求 (本方案)

這些緣由阻礙了咱們 mock 的腳步:git

  • 須要各類配置,用起來太麻煩
  • 學習成本高,配置不對不生效
  • React-Native小程序 等環境用不了
  • 移動端真機調試 mock 不了
  • 對項目新人有學習和配置成本

本方案的優點:github

  • 無複雜配置
  • 0 依賴
  • 傻瓜式的使用體驗
  • 基於老項目改造很容易 (我用了不到10分鐘)
  • mock 數據在項目集中管理維護
  • 即配置即生效
  • 支持全部類型的請求 (xhr fetch wx.request ...)
  • 不影響打包體積
  • 生產環境不受影響

方案原理

本質上仍是請求攔截,並不是新花樣,重點在後面的細節的處理技巧,會讓這個 mock 方案變得絲滑易用web

常見的請求好比 axiosfetchwx.request,並非全部請求都有攔截器方法,何況攔截器也不是萬能的,一般咱們的項目都會進行 請求再封裝,作諸如如下的事情:chrome

  • 根據環境填充 baseURL
  • cookie / storage 搬運
  • 異常狀況彈窗
  • 移動端維護請求隊列顯示 loading 菊花
  • fetch 提供 timeout
  • 今天的主角 mock

好比咱們封裝 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 數據集,好比,咱們在 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 的技巧

如何開啓、關閉 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

  1. 在 mock 文件夾下建立文件,內容如
// xx.js 文件
export default [
  { url: '/user/id', data: 10086, code: 0 },
  { url: '/user/name', data: 'wyf', code: 0 },
];
複製代碼
  1. yarn mock (具體根據你剛纔配置的命令

其餘深刻的探討點

至此,主要內容已經完成,若是還想在項目中繼續優化探索,能夠繼續看下面的部分

是否 gitignore mock 文件夾

ignore 以後,mock 文件夾不會被 git 倉庫收錄,也就是多人合做開發的時候,你們的 mock 文件互不干擾。

我我的理解:

  • 適合 git 收錄的場景:你的項目過於依賴 mock,好比後端環境常常掛,或者在瀏覽器環境沒有宿主提供的鑑權信息(如 app 會給 webview 帶一些登錄 token 等),致使沒法獲取數據
  • 不適合 git 收錄的場景:好比你的項目只在新需求新增幾個接口,供後端沒有 ready 時 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;
複製代碼

是否支持隨機生成 mock 數據等進階需求

本方案是一個很是基礎的工程方案,同時因爲它沒有過多依賴,因此很是靈活,你徹底能夠在攔截階段,對獲取的數據進行處理,好比自定義一些模板語法

本方案的優點,在於靈活,不只僅對 XHR 請求進行攔截,任意環境,好比小程序、fetch 等

github推文地址-js實踐

相關文章
相關標籤/搜索