利用mock提高效率
談到mock,就不得不講前后端分離。理想情況下前后端不分離,由全棧的人以product和infrastructure的維度進行開發,效率是最高的。近些年來業務的復雜度越來越高,真正的全棧人才極為難招,企業只能退而求其次,對開發進行分工細化,讓每個人做自己最擅長的事,前端負責UI顯示和交互,后端負責業務的邏輯、性能等,從而架構上達到更高的效率。同時由於分工的細化,導致前后端的溝通成本增加,代碼的控制權通常在后端,一次小的修改可能導致前后端反復溝通,降低開發效率,就產生了前后端分離的方案。前后端分離通過約定好協議,使用約定的協議進行並行開發,將溝通最后階段放在聯調,節省了大量的時間。
分離的表現主要是視圖層的控制交給前端,對於一些偏應用類項目,使用ajax請求,前后端各負責自己的部分,直接達到分離狀態,而一些展示類系統,受到seo和用戶體驗的影響,分離只能達到部分分離,如fis方案。不管是哪種狀態的分離,都需要進行協議約定,以達到業務的並行開發。對於前端來說,開發的效果更多依賴於數據,想要最大程度的減少聯調時間,就需要根據協議生成數據,這也就是mock的需求。
使用mock開發流程
這里借用yapi的流程圖(yapi表示mock服務):

首先,前后端進行定制接口,定制完成后各自進行開發。前端的開發者使用mock數據進行開發,開發完成后進行真實環境的聯調,找出開發中的問題,再進行測試、上線等流程。
常用的mock方式
1. 代碼層硬編碼
直接數據寫在代碼中(或者獨立的文件,代碼中手動引入),修改代碼跳過接口請求,直接使用數據。這種方式的最大問題是耦合性太強,業務代碼中混雜了mock數據,每次正式提交都要保證代碼數據指向是正確的,否則會產生神奇的結果。同時不是真正發出網絡請求,和真實環境有差異。建議僅在沒有其他方式的情況下才使用。
2. 代碼中針對使用的ajax庫攔截
這種方式分為攔截到本地和攔截到其他服務器。相較於硬編碼,攔截式降低了mock數據和業務代碼的耦合性,只需要引入攔截的代碼,將請求轉發至本地,至本地文件或其他服務器。這種方案的主要問題是需要針對代碼使用的各種庫進行定制插件,初期成本高;同時有部分的代碼入侵,需要保證入庫代碼正確。
-
攔截至數據文件:
最典型的是Mock.js。這種方式實現了自由編寫數據,靈活性比較大,而且mock的數據文件可以同步至倉庫中,下次開發時。缺點是同樣非網絡請求,真實性不足。 -
攔截並改變請求:
這種方式之所以出現是因為服務器的URL和本機的URL規則不同,需要按照規則進行轉換。這種方式與真實情況比較接近,同時產生了跨域,需要服務端提供對應的header。
3. 為所有接口統一添加前綴
這種方式在公用文件中直接添加接口前綴,依賴於特定的庫功能(或者達到相同效果的代碼結構)。同之前的攔截並改變請求類似,但更為簡單一些,只改前綴就可以了,同樣有代碼入侵的問題。
4. 使用其他工具
這種方式分為兩種:使用本地或軟件數據和代理轉發
-
使用本地或軟件數據:
最常用的如:fiddler, charles, whistle等,可以將請求返回指定內容。主要問題是配置比較繁瑣,所有操作都在本機,同步困難。 -
代理轉發:
這種方式和真實環境極為接近,畢竟純靜態的文件,在服務端也是需要進行代理轉發的。可使用的工具就比較多了,使用支持代理轉發的開發工具,或者使用上面提到的工具,同樣能夠將數據轉發出去。這種方式最大的優點是:無跨域、能發出真實請求、與業務代碼完全隔離。
方案對比
對比以上幾種方案,最優的選用代理轉發方式,其次是代碼攔截方式。攔截至數據文件有個優勢就是可以將mock數據和倉庫同步,但在不同分支開發時Mock數據可能會沖突。代理轉發和攔截至服務器使用最方便,但它需要mockServer。只視開發情況而定。
一些mock用工具
mock的實現非常多,基本上每套完善的前端開發工具都是自帶mock的機制。近幾年隨着nodejs的流行,前端可以非常方便的實現自己的mockServer,所以想列舉出所有的還是很困難的,這里介紹一些知名字較高的工具。
1. swagger
- 地址:[https://swagger.io/]
- 介紹: 提到mock,就不得不提到swagger (https://swagger.io/)。它是一個極為流行的一個API設計開發工具,覆蓋了從設計到文檔到測試部署。它是這樣介紹的:
Swagger is the world’s largest framework of API developer tools for the OpenAPI Specification(OAS), enabling development across the entire API lifecycle, from design and documentation, to test and deployment.
在設計RESTFUL類型的API極為有用,它沒有專門提供mock服務,但可以提供mock服務的server模板代碼,可根據模板自行搭建mock-server。此外,還提供相關的API的JSON結構數據,配合相應的工具來實現類似的效果。
2. Mock.js
- 地址:https://github.com/nuysoft/Mock/tree/refactoring
- 介紹:THX團隊出品,本地文件編寫mock數據規則,適用於代碼庫攔截,之前較為流行的一個方案,很多工具都集成了Mockjs語法,目前倉庫已經不再更新。
3. rap2
- 地址:https://github.com/thx/rap2-delos
- 介紹:同THX團隊出品,是rap0.x的升級版本,使用了nodejs和關系數據庫開發,兼容mock.js語法。界面比較簡潔,交互友好,支持界面式編輯API。但URL設計與原URL不同,需要使用代碼庫攔截方式。
4. apiary
- 地址:https://app.apiary.io/
- 介紹:這款在國外用的比較多一些,功能也是比較強大:
Apiary.io平台具有協同設計、即時API模擬、快速生成源碼、自動測試和代碼調試的開源設計工具,最重要的是可以在線模擬測試,因為該平台具備模擬服務器測試服務,可以把設計好的程序在線測試、驗證。
5. easy-mock
- 地址:https://easy-mock.com/
- 介紹:搜車出品,rest api模擬,基於mockjs語法,能夠從swagger生成簡單數據。
6. yapi
- 地址:http://yapi.qunar.com/getfamiliar.html
- 介紹:這是去哪團隊做的,使用了nodejs+mongodb方式開發,主要特性:
- 基於 Json5 和 Mockjs 定義接口返回數據的結構和文檔,效率提升多倍
- 扁平化權限設計,即保證了大型企業級項目的管理,又保證了易用性
- 類似 postman 的接口調試
- 自動化測試, 支持對 Response 斷言
- MockServer 除支持普通的隨機 mock 外,還增加了 Mock 期望功能,根據設置的請求過濾規則,返回期望數據
- 支持 postman, har, swagger 數據導入
- 免費開源,內網部署,信息再也不怕泄露了
rap2和yapi的一些對比
接入mockServer
有了各種極為方便使用的mockServer,想要接入就很簡單了。mockServer的一般使用規則:
- URL相同,直接代理轉發即可
- URL后半部分相同,直接加上URL前綴即可
- URL規則不同,或需要身份認證,需要攔截轉發
攔截式
攔截式針對的是使用一些封裝過的ajax庫,比如jquery、axios,或者使用fetch庫。實現的方式各有不同。如果使用的是支持攔截器模式(如axios),攔截代碼就比較簡單了,直接在攔截器中改變URL指向即可,指向mockServer或mockjs文件。見以下代碼(以下代碼是摘自rap或rap2等其他庫)。
function wrapAxios(axios) {
var url = ''
var oldRequest = {}
var routePassed = false
axios.interceptors.request.use(function (config) {
url = config.url
config.url = "http://" + ROOT + '/mockjsdata/' + projectId + url;
oldRequest = Object.assign({}, config)
return config;
}, function (error) {
return Promise.reject(error);
});
axios.interceptors.response.use(function (res) {
return res;
}, function (error) {
return Promise.reject(error);
});
}
對於一些不支持攔截器的,或者原生的方法fetch,通過覆蓋的方式實現(https://github.com/wenlonghuo/rap2-delos/blob/master/public/libs/fetch.rap.js):
;(function (RAP, fetch) {
if (!fetch) {
console.warn('當前環境不支持 fetch')
return
}
if (!RAP) {
console.warn('請先引入 RAP 插件')
return
}
let next = fetch
let find = (settings) => {
for (let repositoryId in RAP.interfaces) {
for (let itf of RAP.interfaces[repositoryId]) {
if (itf.method.toUpperCase() === settings.method.toUpperCase() && itf.url === settings.url) {
return Object.assign({}, itf, { repositoryId })
}
}
}
}
window.fetch = function (url, settings) {
// ajax(settings)
if (typeof url === 'object') {
settings = Object.assign({ method: 'GET' }, url)
} else {
// ajax(url) ajax(url, settings)
settings = Object.assign({ method: 'GET' }, settings, { url })
}
var match = find(settings)
if (!match) return next.call(window, url, settings)
let redirect = `${RAP.protocol}://${RAP.host}/app/mock/${match.repositoryId}/${match.method}/${match.url}`
settings.credentials = 'include'
settings.method = 'GET'
settings.dataType = 'jsonp'
console.log(`Fetch ${match.method} ${match.url} => ${redirect}`)
return next.call(window, redirect, settings)
}
})(window.RAP, window.fetch)
使用這些插件的方法很簡單,直接在html最后添加指向的script標簽即可(部分攔截可能需要引入多個標簽)。
修改全局URL式
這種情況適合mockServer請求中需要添加baseURL的類型。對於支持baseURL類型的庫,設置baseURL即可。如baseURL為:
http://yapi.demo.qunar.com/mock/1304
我們業務代碼中請求的api為:
/weather/api
那么我們實際請求的地址是:
http://yapi.demo.qunar.com/mock/1304/weather/api
所以我們應該這么設置(以axios為例):
export default axios = new Axios({
baseURL: process.env.NODE_ENV === 'development' ? 'http://yapi.demo.qunar.com/mock/1304' :
})
如果是不區分環境的情況下,需要在提交前將baseURL設置為空,以免影響倉庫代碼。
對於不支持baseURL的庫,建議封裝方法,單獨保存baseURL。
代理轉發式
代理轉發實現的前提是你使用的開發工具支持轉發,如果不支持,就需要使用Fiddler、charles等工具進行規則重寫。下面舉一些例子:
webpack-dev-server中:
proxy: {
"/api": "http://localhost:3000"
}
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true
}
}
注:changeOrigin是http-proxy設置選項,表示在請求頭中將host轉換為目標服務器的地址或IP,解決服務器出現請求地址找不到的問題。
nginx(應該沒人用吧):
location /api {
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_pass http://localhost:11011;
proxy_redirect off;
}
fiddler中在右側的tab頁中選擇autoResponder標簽頁。編輯rule分別輸入
REGEX:^https://server\.example\.com/(.*)
http://www.target.com:3838/$1
mock的一些問題
- mock不能替代聯調。盡管mock數據再真實,也無法實現和后端接口一樣的邏輯,部分邏輯檢查或業務的問題只能聯調才能發現。
- mock不能替代測試。mock本身是為了加快開發速度,那些對於代碼質量要求比較高的項目,mock服務只能提供一些case。
總結
使用mock前提是前后端有一個明確的接口協議,利用合適的工具才能提高開發效率。強大的mock服務可以使你對開發的代碼更為自信,即使沒有后端,新手就可以通過mock熟悉之前的業務界面,或者查看一些表現特殊的界面,最大程度減少對后端的依賴。
上面介紹的幾款mockServer有幾種都是使用nodejs開發的。和其他語言開發的mock服務相比,json格式成為書寫的主要格式,雖然用起來容易,但書寫上並不方便(json5格式在某種程度上增強了書寫體驗,但相比yaml等格式還是有所不足)。同時業務情況不同,針對性的選擇不同的平台,推薦使用rap2和yapi,前者界面更為簡潔,操作方便,后者功能更為強大。如果有特殊的需要,可以自己寫一個,順便練練手。
搭建一個屬於自己的server
參考
【你是如何構建 Web 前端 Mock Server 的? - 莫池宇的回答 - 知乎
https://www.zhihu.com/question/35436669/answer/235608128】
