騰訊搶金達人項目中的前后端協作


在前后端的協作過程中,通常都是並行開發的狀態,那么在后端接口還沒有開發完畢時,前端的業務邏輯工作就很難展開。因此也就有很多模擬接口數據的方式,這些方式各有個的優缺點:

  • 直接在代碼中模擬接口數據:侵入業務邏輯,在后期需要刪除這些模擬數據;
  • fiddler 替換文件:頁面接口比較多時,需要替換的文件比較多;
  • fs 模塊讀取 json 文件:若是長列表的話,需要造的數據很多;
  • mockjs:避免上述方式的缺點,但無法校驗參數是否缺失;
  • service worker:基於 service worker 可以攔截前端的請求,並構建假數據返回,但無法攔截 node 端發起的請求;
方式 校驗參數合法性 切換環境方便 前后端請求均可 不修改業務代碼 模擬數據方便
直接在業務代碼
中寫接口數據
fiddler 替換文件 yes
fs 讀取 json 文件 yes yes
mockjs yes yes yes
sw yes

我們理想的狀態是:

  1. 提前校驗請求接口中參數的合法性,是否缺失某些參數等;
  2. 切換環境方便,既可以使用模擬數據,也可以使用測試環境中的數據,同時也可以用正式環境中的數據進行檢驗;
  3. 可以攔截前后端均發起的請求,並盡量少的修改業務代碼;
  4. 生成的模擬數據方便,假如接口中要返回前 1000 名用戶的數據,總不能在 json 文件中寫 1000 條數據;

上面的這幾種方式,在我們搶金達人項目中,均不適用,或者對原有邏輯改動太大,或者使用起來不方便。這里我根據我們項目的需要,基於 mockjs 並與 express 的結合,實現了一套模擬數據的方法。

1. 模擬數據並校驗參數的合法性

把接口的數據全部寫在 json 文件,然后通過 fs 模塊進行讀取的這種方式,在構造大量數據時非常不方便。因此我們基於 mockjs 來實現模擬的數據,幾行代碼就能實現排行榜等大量的模擬數據,同時,也可以模擬一些稍微極端的情況,例如用戶的昵稱長度過長等,這些數據在測試環境一般很少能遇到,或者在后端接口模擬的成本也會比較高。

// rank-person.js
Mock.mock({
  "rank|1000": [
    {
      "no|+1": 1,
      uin: () => Mock.Random.string(32),
      nick: () => Mock.Random.string(1, 20),
      face: () => faces[Mock.Random.integer(0, 3)],
      region: "INVALID",
      title: "初露鋒芒",
      level: () => Mock.Random.integer(1, 20),
      score: () => Mock.Random.integer(1, 2000),
      winPercent: 86
    }
  ]
});

但是,純基於 mockjs 數據的方式,我們無法提現獲知接口參數的異常。當我們在匹配到接口請求后就返回數據,會降低對參數的敏感度。這里,我對配置文件進行改造,當前接口中需要的參數提前設定好,類似於 jQuery.validate 中的設定。

這里我們的排行榜接口里有個last參數,0 表示是本周的數據,1 表示是上周的數據:

module.exports = {
  params: {
    last: {
      required: true, // 是否必須
      type: "number", // 參數的類型
      defaults: 0, // 默認值
      min: 0, // 最小值
      max: 1 // 最大值等
    }
  }
};

當 mock server 接收到請求后,會先校驗參數的合法性,若參數不合法直接返回。其實我們排行榜的 last 參數不是必傳項,不傳時即默認是 0,但我們在這里測試時改為必傳,只要不傳 last 參數即為參數不合法:

mock校驗參數-蚊子的博客

當參數校驗通過后,才會返回后面模擬的數據:

mock模擬的數據-蚊子的博客

2. 數據環境的切換

我們在上面的圖中可以看到,當 mock 字段為"mock"時,讀取模擬的數據,是不是一定要加一個 mock="mock"的參數才能去讀模擬的數據呢?

這個要看咱們項目到什么狀態了,當項目還在前提開發階段時,大部分接口都還沒有完成,這里我們可以將接口默認指向到模擬數據,mock="testing"時就指向到測試環境的接口;當項目已穩定上線了,在迭代更新的階段時,大部分接口已經存在和完善了,只有部分的接口需要進行模擬調試,這時我們用mock="mock"參數來指向到模擬數據。

這里我使用 mock 參數控制,還有一個原因是,前端項目會根據當前是哪個環境,自動請求對應環境的接口,所有的接口均是統一控制的:

接口的統一配置-蚊子的博客

如上圖所示,如果我們要通過環境變量控制 api 字段,最終強行修改某個接口變相請求其他環境的數據,會造成其他接口數據的混亂,最終可能的結果是頁面整體會掛掉。我們在使用mock字段作為參數時,侵入的業務邏輯會維持到最小的程度,同時,也能把前后端的協作,縮小到單個接口的粒度。而且,mock 參數也只會在 local 環境生效,即使忘了去掉這個參數,也不會對線上環境造成影響。

再有,當我們前端邏輯發生變化后,除了使用模擬數據來檢驗,如果線上有接口,我們也想用線上的數據檢驗一下。可以,當傳入mock="production"的參數時,mockServer 會讀取線上接口的數據並返回。

3. mock 與 json

有的同學會創建一個 json 文件,把接口需要的數據都放到這個 json 文件里,然后使用fs.readFile來讀取。這樣倒是也可以,但是當接口數據多的時候怎么辦,例如有一個排行榜是要輸出前 100 名、前 1000 名用戶的數據,總不能復制出 1000 份用戶的數據吧。

在當前最好的方式,就是使用 mockjs 的工具來生成數據了,可以任意的隨機,也可以生成任意個數的數組數據。我們這里把對參數的校驗和 mock 生成的模擬數據放到一起:

module.exports = {
  params: {
    last: {
      required: true, // 是否必須
      type: "number", // 參數的類型
      defaults: 0, // 默認值
      min: 0, // 最小值
      max: 1 // 最大值等
    }
  },
  result: {
    code: 0,
    msg: "success",
    "x-from": "mock",
    data: Mock.mock({
      "rank|100": [
        {
          "no|+1": 1,
          uin: () => Mock.Random.string(32),
          nick: () => Mock.Random.string(1, 20),
          face: () => faces[Mock.Random.integer(0, 3)],
          region: "INVALID",
          title: "初露鋒芒",
          level: () => Mock.Random.integer(1, 20),
          score: () => Mock.Random.integer(1, 2000),
          winPercent: 86
        }
      ]
    })
  }
};

params 字段表示必須的參數,result 表示要返回的數據,data 中即為我們偽造的數據。

4. 實時修改模擬數據

我們可以在頁面中直接實時修改數據,然后進行保存,自動刷新頁面后,就會得到剛才想要的結果。

實時修改模擬數據-蚊子的博客

我們在頁面中 mock 的接口都會在調試面板中展出來,默認調試面板時關閉的,開發者在點擊頁面右下角的灰色按鈕后,可以呼起調試面板。修改里面的數據保存后,就實時看到剛才的效果。

5. 總結

在開發和后期維護的過程中,前端會經常遇到模擬接口數據的情況,要么是接口還沒有開發完成,要么是接口不太好返回我們想要的狀態。我也是在對比多種方案后,選擇了適合當前項目的一種前后端協作方案。

歡迎關注我的公眾號,多多交流:
蚊子的博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM