您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么優雅的實現前端請求Mock”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“怎么優雅的實現前端請求Mock”文章能幫助大家解決問題。
背景
在我們前端的日常工作中,難免需要請求后端的接口獲取數據來進行頁面的渲染,這個時候我們前端靜態頁面的開發以及相應的邏輯處理都與后端的接口息息相關,所以絕大多數時候在新需求的前期,我們前后端需要統一一份接口文檔,以此來作為各自開發的指導和約束。
接口定義好入參、返參之后我們就可以在前端自己mock數據進行相關的開發,比如有一個定義好的查詢商品列表的接口,我們很自然的就能依據接口文檔寫出如下代碼
// 定義api
const getProducts = ()=>{
return axios.get('/products')
}
// 組件中使用
getProducts()。then(res=>{
this.productList = res.data
})
但是在一開始,后端往往只是定義了一個接口格式,并沒有實現其中的業務邏輯,或者是并沒有部署,為了讓頁面有數據方便開發,我們可能會按照以下兩種方式進行mock
修改定義api的部分
const getProducts = ()=>{
return Promise.resolve([
{id:001,name:'商品1'}
])
}
修改調用部分
getProducts()。then(res=>{
// this.productList = res.data
})。finally(()=>{
this.productList = [
{id:001,name:'商品1'}
]
})
可以看出無論哪種方式,我們都要修改接口相關處的代碼,等到接口真正可用時我們又得刪除這一部分無用的mock代碼,如果有幾十個接口,重復的工作量大不說,而且容易遺漏,接下來我們就一起來嘗試如何優雅實現前端mock
實踐
因為目前公司項目普遍以vue為主,所以接下來的兩個方案都是以vue/cli 4.x版本構建的項目進行介紹(react同理,配置webpack即可),http請求使用的主流的axios
方案一 利用axios的adpter
axios作為一款優秀的http庫,能兼容在瀏覽器環境以及node環境發送http請求,核心就是其內置adpter模塊會根據運行環境在瀏覽器端使用XMLHttpRequest,在node環境使用其內置http發起請求,并且支持配置自定義adpter,方案一就從這里入手
從場景出發,我們在本地開發的時候是否需要使用mock顯然是一個需要全局可控的操作,可以簡單的進行開關,最合適的方式就是從腳本區分
添加dev:mock腳本,區分dev,開啟全局mock
// package.json
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"dev:mock": "cross-env IS_MOCK=true vue-cli-service serve",
},
cross-env幫我們處理兼容問題,向process.env中注入IS_MOCK變量
修改webpack配置,省略其余不相關配置
// vue.config.js
const IS_MOCK = process.env.IS_MOCK === 'true' // 當運行dev:mock時為true
module.exports = {
// 利用webpack definePlugin注入全局變量
chainWebpack: config => {
config.plugin('define')。tap(args=>{
args[0].IS_MOCK = IS_MOCK
return args
})
},
}
添加mock配置文件
module.exports = {
'/products':{
code:0,
data:[
{id:001,name:'商品1'}
],
message:'success',
}
}
封裝axios實例
import axios from "axios";
// 讀取mock配置
const mockMap = require("xxxxxx/mock.js"); // mock配置文件路徑
var instance = axios.create({
adapter: (config) => {
// mock配置中匹配到當前url并且開啟mock啟用自定義adpter
if(mockMap[config.url] && IS_MOCK){
return Promise.resolve({
data: mockMap[config.url],
status: 200,
});
}else{
// 否則調用默認的適配器處理,需要刪除自定義適配器,否則會死循環
delete config.adapter;
return axios(config);
}
},
});
export default instance
定義api采用配置后的axios實例
// api.js
import instance from 'xxxx' // axios實例路徑
export const getProducts = ()=>{
return instance.get('products')
}
組件中使用
import { getProducts } from 'api.js'
getProducts()。then(res=>{
this.productList = res.data
})
使用這種方案后續只需要維護mock.js文件,把接口文檔相應的內容配置上就可以實現本地開發請求的mock,可以運行dev或者dev:mock來決定是否啟用mock
** 這種模式類似于上面修改定義api的方式實現mock,方便之處在于可以全局統一處理,實際上并不會發送http請求。
方案二 proxy + express
方案一雖然實現了簡單的mock功能,但實際上思考一下還是有幾處不夠合理的地方:
并沒有實際發送http請求,對請求的整個鏈路沒有做到盡可能真實的模擬
依賴于axios庫,其它http庫能否實現以及實現是否像axios一樣輕便沒有探究,因此可拓展性較差
功能耦合,mock作為一個額外的功能模塊,把其嵌入到axios中處理,會使得axios變得略顯臃腫
基于以上幾個問題,于是有了使用express啟動一個代理服務器,接收我們前端應用的請求來進行mock相關的處理,模擬一個完整的http請求閉環的想法
首先在項目根目錄下新建一個server.js文件,用來寫我們代理服務器的代碼
const express = require("express");
// 引入mock配置文件
const mockMap = require("./mock");
const app = express();
app.use(express.json());
app.all("*", requestFilter, async (req, res, next) => {
const { path } = req;
res.send(mockMap[path]);
});
// 端口和 webpack prxoy中設置對應上,可以隨意設置一個未被占用的端口
app.listen("3306", () => {
console.log("serve is running at port 3306");
});
通熟易懂,就是監聽3306端口,攔截所有請求,返回mock數據
修改webpack的proxy配置,使得運行mock腳本時,請求打到我們的代理服務器上,省略其余不相關配置
// vue.config.js
const IS_MOCK = process.env.IS_MOCK === 'true' // 當運行dev:mock時為true
module.exports = {
devServer: {
// …
proxy: {
// 所有匹配/api的請求會被轉發到target
'/api': {
// 開啟mock時 設置target為本地代理服務地址,這里端口3306與serve中監聽端口保持一致
target: IS_MOCK ? 'http://localhost:3306/' : 'xxxxx',
ws: true,
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
},
}
},
}
修改一下上文創建axios實例的代碼instance.defaults.baseURL = "/api",設置一下所有請求的baseURL,以此來使proxy配置匹配所有請求
完成上述配置,我們重啟前端應用,并且啟動我們的服務端代碼就可以使用mock功能了
,我們可以修改一下腳本,方便當我們開啟mock功能的時候自動啟動serve服務
// package.json
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"dev:mock": "cross-env IS_MOCK=true vue-cli-service serve & node serve.js",
},
其實到這里,mock的功能已經可用了,我們還可以基于現有的代碼結合自身的需求的進行拓展
拓展
我在項目中使用的時候進行了一點點拓展,給大家提供一個拓展的參考
修改mock配置文件
module.exports = {
'/products':{
code:0,
data:[{
id:1,
name:'商品1'
}],
message:'success',
config:{
method:'GET',
delay: 1000,
validator:(request)=>{
const error = {
status:400,
msg:'請檢查參數是否合法!'
}
const success = {
status:200,
msg:'success'
}
// 假設該請求需要一個必傳參數 timestamp
return request.query.timestamp ? success : error
},
}
},
}
加了額外的config字段進行一些差異化配置,例如指定響應延時 1000ms 必傳參數校驗等
修改serve端代碼
const express = require("express");
const mockMap = require("./mock");
const app = express();
app.use(express.json());
// 請求過濾器
const requestFilter = (req, res, next) => {
const { path,method } = req;
// 設置相應頭 處理中文亂碼
res.set('Content-Type', 'text/plain')
// 404 提前過濾
if (!mockMap[path]) {
res.status(404)。end();
}
// 請求方法不匹配提前過濾
if(method !== mockMap[path].config?.method ?? 'GET'){
res.status(405)。end('請檢查請求方法是否正確!')
}
// 自定義校驗規則不匹配過濾
if (mockMap[path].config && mockMap[path].config.validator) {
const data = mockMap[path].config.validator(req);
if (data.status !== 200) {
res.status(data.status)。end(data.msg)
}
}
setTimeout(() => {
next();
}, mockMap[path].config?.delay ?? 0);
};
app.all("*", requestFilter, async (req, res, next) => {
const { path } = req;
// 移除config字段
const { code,data,message } = mockMap[path]
res.send({code,data,message});
});
app.listen("3306", () => {
console.log("serve is running at port 3306");
});
關于“怎么優雅的實現前端請求Mock”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。