前端開發如何作好本地接口模擬

前端開發如何作好本地接口模擬

以前有寫過一篇 本地化接口模擬、先後端並行開發,講到過本地接口模擬,但不太細緻。此次細細的說說本地接口模擬。css

1. 有什麼好處

本地接口模擬最大的好處就是可以使先後端項目解耦,前端更專一於開發,減小線上調試,以此提高開發效率。html

2. 有哪些途徑

本地接口模擬通常分爲工具層面和代碼層面。前端

3. 工具層面

就工具層面而言,通常是由項目的構建工具提供的功能。好比,當咱們用 webpack-dev-serverwebpack-dev-middleware + browser-sync 等工具時,就能夠向工具裏添加本地接口模擬功能。node

注:這裏不講解工具如 webpack-dev-serverwebpack-dev-middleware + browser-sync 等的用法,若有須要,能夠本身去了解一下

下面以 webpack-dev-server 爲例進行講解,其餘工具相似。webpack

3.1 靜態文件

最簡單的,咱們能夠用靜態 json 文件作本地接口模擬功能。git

|-- /                        # 項目根目錄
  |-- mock/                  # 模擬數據目錄(能夠自定義)
    |-- 1.json
    |-- 2.json
    |-- ...
    
  |-- ...                    # 其餘文件

而後用 webpack-dev-server 以項目根目錄爲基地址來開啓本地開發調試,在頁面中就能夠這樣訪問:github

fetch('/mock/1.json');       // 訪問 1.json 
fetch('/mock/2.json');       // 訪問 2.json

# 能夠將 fetch 換成其餘請求方式

這種方式能夠訪問項目中全部的文件,不光是 json 文件,其餘的如 htmljscss 之類的文本文件、如圖片之類的二進制文件也能夠訪問。另外,只要文件有更新,刷新瀏覽器頁面就能夠從新獲取新的文件,沒有緩存。web

由於本地接口模擬功能主要是針對的返回值爲 json 格式的異步請求,因此這種方式主要用 json 文件。ajax

這種方式是最簡單、快捷、使用難度最低的方式。json

3.2 動態註冊接口

使用靜態文件作本地接口模擬功能主要存在如下的一些問題:

  1. 靜態文件只能以 get 方法訪問
  2. 輸入數據是靜態的,不能作運算、循環、判斷等,也不能根據請求參數作出不一樣的響應
  3. 本地接口名與服務器上的接口名不同,這就比較麻煩了,每次上線到服務器的時候都得改接口名

因此,多數狀況下,都會採用動態註冊接口的方式作本地接口模擬功能。

這種方式是用 js 文件編寫一系列的 路由 => 響應 映射,而後動態的把定義好的接口註冊到工具實例中。

目錄結構:

|-- /                        # 項目根目錄
  |-- mock/                  # 模擬數據目錄(能夠自定義)
    |-- user.js
    |-- home.js
    |-- ...
    
  |-- ...                    # 其餘文件

示例 mock 文件的寫法(能夠自定規範,下面只是演示):

# mock/user.js

module.exports = {
  'GET /user/profile': { ... },                  // 直接返回一個對象
  'POST /user/update': (req, res) => { ... },    // 根據 `req, res` 的自定義響應
  ...
};


# mock/home.js

const mockjs = require('mockjs');

module.exports = {
  'GET /home/list': mockjs.mock({                // 用 mockjs 輔助生成假數據
    'list|1-10': [{ 'id|+1': 1 }],
  }),
};

注:

  1. 上面的寫法只是示例,能夠自定規範、書寫格式等
  2. 能夠用 mockjs 庫來幫助生成假數據

webpack-dev-server 的相關配置:

# webpack.config.js

const beforeDevServer = app => {

  // 在這裏讀取 mock 目錄下的全部文件,按照必定的規範和格式,載入動態接口
  // 好比:
  
  app.get('/user/profile', function(req, res) {
    res.json({ ... });                           // 返回文件中定義的 `GET /user/profile` 的值
  });
  
  app.post('/user/update', function(req, res) {
    handle(req, res);                            // handle:文件中定義的 `POST /user/update` 的自定義處理函數
  });
  
  ...
};

module.exports = {
  //...
  devServer: {
    before: beforeDevServer,
  }
};

而後用 webpack-dev-server 開啓本地開發調試,在頁面中就能夠這樣訪問:

fetch('/user/profile');                          // 訪問 /user/profile 
fetch('/user/update', {method: 'post', body: { ... }});
                                                 // 訪問 /user/update
fetch('/home/list');                             // 訪問 /home/list

# 能夠將 fetch 換成其餘請求方式

通常來講,咱們還會用上 chokidar 來監聽 mock 目錄下的文件變更,來更新路由及其響應,以此可以作到每次訪問到的都是最新的資源(由於 node 針對某個模塊只會加載一次)。

const chokidar = require('chokidar');

const watcher = chokidar.watch('./mock');
watcher.on('change', path => {

  // 先清除模塊緩存,保證加載最新的資源
  if (require.cache[path]) delete require.cache[path];
  
  const mapObj = require(path);
  
  // 接下來把映射對象 mapObj 從新映射到 app 中
  ...
});

這種方式比較複雜,尤爲是對項目搭建者要求比較高,須要對相關工具備深刻的瞭解,好在社區已經有封裝好的工具:roadhog

但這種方式對使用者是很棒的,由於可以徹底模擬服務器接口,包括接口名、HTTP 方法、參數、返回值等,因此一樣的代碼既能夠在本地運行,也能夠在服務器上運行。

因此,這也是比較推薦的方式。

注:上面的代碼只是演示構建過程,並不保證能夠運行

3.3 使用代理

這種方式是把本地模擬文件寫在另外一個單獨項目裏,而後使用使用代理的方式,訪問模擬接口。

mock 項目(以 koa 爲例):

const Koa = require('koa');
const Router = require('koa-router');

const app = new Koa();
const router = new Router();

router.get('/api/user/profile', (ctx, next) => {
  ctx.body = { ... };
});

router.post('/api/user/update', (ctx, next) => {
  // ...
});

app
  .use(router.routes())
  .use(router.allowedMethods());

app.listen(3000);

app 應用項目:

webpack-dev-server 的相關配置:

# webpack.config.js

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': 'http://localhost:3000'            // 把全部 `/api` 開頭的接口都代理到 `mock` 項目中 
    }
  }
};

而後用 webpack-dev-server 開啓本地開發調試,在頁面中就能夠這樣訪問:

fetch('/api/user/profile');                      // 訪問 mock 項目的 /api/user/profile 
fetch('/api/user/update', {method: 'post', body: { ... }});
                                                 // 訪問 mock 項目的 /api/user/update
                                                 
# 能夠將 fetch 換成其餘請求方式

通常來講,咱們還會用上 nodemon 來監聽 mock 項目中的文件變更,自動重啓 mock 應用程序,以此可以作到每次訪問到的都是最新的資源(由於 node 針對某個模塊只會加載一次)。

nodemon app.js

這種方式能夠統一管理多個項目的數據模擬文件,多個項目能夠共享一些模擬數據。

3.4 線上接口模擬

有些時候,當咱們在產品環境的時候(在線上)也可能想用模擬數據(好比APP、微信小程序、用於演示的 web 應用等),或者須要一個線上的地方來統一管理模擬數據時,就須要線上接口模擬了。

線上接口模擬擁有完備的 UI 操做界面,能夠添加多個用戶、多個團隊、多個倉庫,能夠生成爲每個請求參數添加類型限定、描述,爲響應數據字段添加描述等。

由於是在線上的模擬數據,因此在任何地方均可用,不論是本地開發,仍是線上調試、演示,都是可用的。

比較有名的線上接口模擬工具備:

3.4.1 easy-mock

圖片描述

環境需求:Node.js (>= v8.9) & MongoDB (>= v3.4) & Redis(>= v4.0)

安裝步驟請參考官方的文檔 easy-mock#quick-start

easy-mock 主要提供瞭如下的一些功能:

  • 支持接口代理
  • 支持快捷鍵操做
  • 支持協同編輯
  • 支持團隊項目
  • 支持 RESTful
  • 支持 Swagger | OpenAPI Specification (1.2 & 2.0 & 3.0)

    • 基於 Swagger 快速建立項目
    • 支持顯示接口入參與返回值
    • 支持顯示實體類
  • 支持靈活性與擴展性更高的響應式數據開發
  • 支持自定義響應配置(例:status/headers/cookies)
  • 支持 Mock.js 語法
  • 支持 restc 方式的接口預覽

3.4.2 RAP / rap2-delos + rap2-dolores

圖片描述

環境需求:Node.js (>= v8.9) & MySQL (>= v5.7) & Redis(>= v4.0)

RAP 目前有兩個版本,第一個版本的 RAP 已經被官方廢棄了,建議用第二個版本。

RAP2 分紅了兩個包:

RAP2 的安裝步驟要麻煩一些,rap2-delos 能夠參考官方文檔 rap2-delos#部署非官方rap2-delos部署文檔rap2-dolores 能夠參考官方文檔 rap2-dolores#deployment-部署

RAP2 提供了與 easy-mock 相似的功能,但比 easy-mock 要更強大一些,固然也要複雜一些,好比:

  • 對請求參數支持更完備,好比 headersquery paramsbody params
  • 對響應數據支持更完備,好比能夠爲每一個字段添加描述、限定類型等

3.4.3 二者之間比較

RAP2 比 easy-mock 要更強大一些,但也要複雜一些,因此追求功能完備的能夠用 RAP2,追求簡單快捷的能夠用 easy-mock

4. 代碼層面

從上面能夠看出,除了第二種方式 動態註冊接口 以外,其餘的方式都不能作到徹底模擬服務器環境,至少服務器接口地址與本地模擬地址不同,這就有一個問題:每次上線前都得改爲服務器地址。

另外,前端與後端對接的過程當中也老是不免會遇到一些問題:

  • 前端傳的參數與後端所需的參數難以保持一致:好比分頁,前端定的 page、從 1 開始,後端定的 pageNum、從 0 開始
  • 前端須要的數據與後端返回的數據相差很大,須要對返回的數據作處理
  • 後端可能更改字段名或者數據類型,致使前端要查找、並更新相應的代碼

一種好的、解決這些問題的方式是對應用進行分層、把異步請求進行隔離封裝。

我通常會用 see-fetchsee-ajax 對異步請求進行隔離封裝。

注:see-fetch 是對 window.fetch 的封裝,see-ajax 是對 XMLHttpRequest 對象的封裝。

4.1 以 see-fetch 爲例進行說明:

能夠在代碼中設置多個內部環境,而後針對不一樣的外部環境設置不一樣的內部環境(如:本地環境、線上環境等),這樣就能夠作到不改代碼,只改一個環境值。

若是搭配 define-plugin,連環境值都不須要改,直接由 define-plugin 在運行的過程當中指定。

import seeFetch from 'see-fetch';

seeFetch.setEnv(0/1/2/3);

seeFetch.setEnv(__SEE_ENV__);          // __SEE_ENV__ 由 define-plugin 運行中指定

配置一個異步請求:

seeFetch.config(name, {                // 定義一個名爲 name 的異步請求(下面的配置能夠是多環境,每一個環境能夠設置不一樣的值)
  method,                              // 當前請求使用什麼 http 方法
  stringify,
  settings,
  url,                                 // 當前請求 url 地址
  req,                                 // 請求參數鍵名的映射,好比 `page => pageNum`
  pre,                                 // 操做請求參數,好比 page 從 1 開始改爲從 0 開始
  refactor,                            // 重構響應數據,如字段重命名、類型轉換等
  post,                                // 操做響應數據,以把數據轉換成本身所須要的數據
  implement,                           // 自定義請求,好比後端返回一個模板字符串,而不是接口
});

發起訪問:

seeFetch(name, params).then(result => {
  // 這裏的 result 是通過格式化後的最終數據 
});

從上面能夠看出:一個請求的不肯定性都被封裝到了配置中,不論是接口地址更新、前端請求參數與後端不一致、後端響應數據與前端所需的差別很大等,均可以在配置中進行操做,而絲絕不須要改其餘地方的代碼。

這樣,若是後端接口有什麼改動的,只須要找到配置文件進行更新,而不用在項目中找哪裏使用了這個接口。

如此,既能很好的使用本地數據模擬,也能夠從容應對後端接口的改動,便能事半功倍。

後續

更多博客,查看 https://github.com/senntyou/blogs

做者:深予之 (@senntyou)

版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證

相關文章
相關標籤/搜索