這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 簡介 大家好,前端小白一枚,目前接觸後臺管理系統比較多,經常遇到不同對象的增刪改查的介面,如何對Api進行一個有比較好的管理是個問題。在學習偏函數的時候有了靈感,想到一個不錯的API管理方案,並應用在項目一個模塊當中,並且開發效率和維護性 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
簡介
大家好,前端小白一枚,目前接觸後臺管理系統比較多,經常遇到不同對象的增刪改查的介面,如何對Api進行一個有比較好的管理是個問題。在學習偏函數的時候有了靈感,想到一個不錯的API管理方案,並應用在項目一個模塊當中,並且開發效率和維護性可讀性都很不錯,和大家分享一下~
當前項目的前端API管理方案
// 封裝的介面 export function obj1Func1(){} export function obj1Func2(){} export function obj2Func3(){} export function obj2Func4(){} // 引入方式 import { obj1Func1, obj1Func2, obj2Func3, obj2Func4 } from 'xxx' // 使用方式 const params = {...} await obj1Func1(params)
當介面多了之後,我們管理介面(查找)是一件很麻煩且廢眼睛的事,需要一直翻,註釋看不過來,維護性和可讀性差。
統一export方式
// 封裝的介面 function obj1Func1(){} function obj1Func2(){} function obj2Func3(){} function obj2Func4(){} // 導出介面 export { obj1Func1,//註釋 obj1Func2,//註釋 obj2Func3,//註釋 obj2Func4,//註釋 ... } // 引入方式 import { obj1Func1, obj1Func2, obj2Func3, obj2Func4 } from 'xxx' // 使用方式 const params = {...} await obj1Func1(params)
優點
- 面向對象,不需要每個介面函數都引入,開發時調用方便
- 簡化操作類型命名,如
update -> upd
,開發和維護方便
缺點
- 當模塊涉及的對象很多,則需要建立非常多的文件,當文件名複雜時,難以看懂維護難度up。
- 當頁面需要引入多個對象,需要引入一個個文件,降低開發效率。
- 找對象需要拖拽到文件最底部
以面向模塊(對象)的方式
假設當前模塊下涉及到 n 個對象及對應的增刪改查介面, 定義一個映射表
// api映射表 const apiMap = { // 公共介面 common: { commonFun1, commonFun2, }, //對象1 dog: { //增刪改查 add: obj1Func1 get: obj1Func2 }, //對象2 cat: { //增刪改查 upd: obj2Func3, del: obj2Func4, }, ... }
apiMap
對象
一級鍵名是模塊涉及的對象
二級鍵名是對象相關的操作類型,值是對應的介面函數
導出方式1
直接導出各個對象
export default { commonApi: apiMap['common'], dogApi: apiMap['dog'], catApi: apiMap['cat'], } import {commonApi,...} from "xxx"
面向模塊(多個對象)
本質就是以對象的方式來進行管理,只不過這裡面向的是模塊。這裡一個模塊只對應一個文件,包含了涉及到的n個對象的介面。因為我覺得一個模塊下建n個對象一長串的Api
文件,又沒法對文件名註釋(文件名總有不認識或拼接的單詞吧)只會帶來更大的維護困難
找了幾個不常見的英語名詞,英語爛仔直接帶上痛苦面具
而有了映射表就相當於有了一個目錄(文件最上方一目瞭然, Map下的對象十分清晰還有註釋),
至少目前我是能都秒讀懂介面含義了
也不會出現面對老項目里那種長得拖不完的不知名介面文件的懵逼,點進去還只有幾行代碼(雪花飄飄~)
優點:
- 同上
apiMap
變成介面目錄,可讀性和可維護性提高(下方介紹)- 涉及同模塊多個對象只需要引入一個文件
缺點:
- apiMap(目錄)和export的位置一個在文件最上方,一個在最下方,瀏覽時非常不方便,依舊需要經常拖拽
這種方式已經比較好用了,從可維護性和可讀性,拓展性來看我更推薦第二種方式
導出方式2
導出一個訪問映射表的函數,參數是對象及操作類型,如(dog, upd)
// 暴露一個訪問api映射表的函數, 參數是對象和操作 // 這裡沒有錯誤處理,jym看懂就行 export default function api(obj, action) { if (action) { // 返回某對象某操作的介面函數,如dogUpdate return apiMap[obj][action] } // 返回一個包含多個操作介面函數的對象或公共介面 return apiMap[obj] } // 封裝的介面 function obj1Func1(){} function obj1Func2(){} function obj2Func3(){} function obj2Func4(){}使用時方式1
import API from 'xxx' // 沒有錯誤處理,主打看懂 async function getData() { const params = {...} const data = await API(obj1, 'get')(params) await API(obj1, 'upd')(params) await API(obj1, 'del')(params) }使用時方式2
import API from 'xxx' const obj1API = API(obj1) const data = await obj1API.get(params) const data = await obj1API.upd(params) const data = await obj1API.del(params)
api
函數
api
函數可以複製或者寫在公共模塊引入就行了,實際上工作量只在維護映射表apiMap
現在查找介面原本一個模塊里可能涉及10個對象共100個介面,順序查找最差情況要看100條函數註釋,而根據對象查找最差情況是10(對象)+10(操作類型)即20條函數註釋。
const apiMap = { ... // 註釋:這是target對象 targetObj: { add: objAddFunc, // 註釋:增刪改查的話可有可無 upd: objUpdateFunc, del: objDeleteFunc, get: objGetFunc, } ... }
只需要關註目標對象(其實是註釋),清晰且一目瞭然,甚至不需要函數註釋,不需要拖到文件底部
可維護性和可讀性
模塊化
這種方式用對象結構拆分也算是模塊化了,看著不太習慣,但一個文件里對象和介面能都讀懂,維護性和可讀性也更好,即便介面函數再多行數再多,其實也只需要看apiMap
封裝
(接下來開始胡扯~)
api函數
封裝了一層,可以統一管理介面提高拓展性和復用性,例如統一給action
為get
的套一個節流函數
// 暴露一個訪問api映射表的函數, 參數是對象和操作 // 這裡沒有錯誤處理,jym看懂就行 export default function api(obj, action) { if (action) { if (action === "get") { return throttle(apiMap[obj][action], 500); // 設置節流時間為 500 毫秒,期間返回空函數 } return apiMap[obj][action] } // 返回一個包含多個操作介面函數的對象或公共介面 return apiMap[obj] }或者當模塊下有多個對象需要增刪改查,且只需要一個參數id,那麼只要再加個定製場景的
api函數
// 偏函數固定參數,這裡constParams假設為 { id:007 } export function paramsApi(constParams, obj) { // otherParams是額外參數,在各自介面做個合併Object.assign() return (action, otherParams) => apiMap[obj][action](otherParams) } // 固定參數 const constParams = { id: 007 } const dogApi = paramsApi(constParams, 'dog') const catApi = paramsApi(constParams, 'cat') // 免參數直接調用即可 dogApi('get') dogApi('update', { color: 6 }) dogApi('delete') catApi('get') catApi('update', { color: 6 }) catApi('delete')
這個場景有些理想化,但有一層封裝也確實能夠在需要時方便統一管理
然後這裡 action: objActionFunc
這裡也算是一層封裝,方便進行命名簡寫和規範
// xxxModule.js const apiMap = { //對象1 obj: { action: objActionFunc add: dogAdd, // xxx/dog/add mAdd: dogImport, // xxx/dog/import upd: dogUpdate, // xxx/dog/update }, }
開發體驗
和同事溝通後,我應用在項目的一個模塊中,感覺很棒!
首先寫介面引入公共模塊的api
函數,定義apiMap
映射表,正常寫介面,工作量多了一個apiMap
罷了
// 引入api,getType函數 import { api } from "common" // api映射表 const apiMap = { ... } // 暴露api函數 export default api // 封裝的介面 ...開發時查找介面就全程對著文件最上方的映射表複製,基本不需要怎麼拖拽,不需要切換文件去找其他對象,也不需要看一堆無關代碼,全程看註釋,大大降低心智負擔,小白上手也能分分鐘找到
// xxxModule.js const apiMap = { //對象1 dog: { add: xxx get: xxx }, //對象2 cat: { upd: xxx, del: xxx, }, }引入介面時只要一個API函數,調用方式基本大差不差吧,反正都是
copy
,然後改下操作類型
import API from "@/api/xxx/xxxModule" // 調用時, 兩種方式,複製個對象名,記住個操作類型,完事 const dogAPI = API('dog') const catAPI = API('cat') await dogAPI.get(params) await dogAPI.upd(params) await catAPI.get(params) // 或 await API('dog', 'get')(params) await API('dog', 'upd')(params) await API('cat', 'get')(params)
開發效率我覺得和對象方式的API管理方案沒啥區別,都是copy
然後改下操作類型,但其實最大的好處還是在可維護性和可讀性上
寫在最後
非常感謝看到最後的jym,這是我本0前端小白通過偏函數產生的一點小想法,感覺挺好用的分享一下(可能場景比較簡單局限後臺系統),歡迎jym多多指點發表建議(玻璃心)