这个前端Api管理方案会更好?
点击小卡片参与粉丝专属福利简介大家好,前端小白一枚,目前接触后台管理系统比较多,经常遇到不同对象的增删改查的接口,如何对Api进行一个有比较好的管理是个问题。在学习偏函数的时候有了灵感,想到一个不错的API管理方案,并应用在项目一个模块当中,并且「开发效率和维护性可读性都很不错」,和大家分享一下~
当前项目的前端API管理方案//封装的接口
exportfunctionobj1Func1(){}
exportfunctionobj1Func2(){}
exportfunctionobj2Func3(){}
exportfunctionobj2Func4(){}
//引入方式
import{obj1Func1,obj1Func2,obj2Func3,obj2Func4}from'xxx'
//使用方式
constparams={...}
awaitobj1Func1(params)
当接口多了之后,我们管理接口(查找)是一件很麻烦且废眼睛的事,需要一直翻,注释看不过来,维护性和可读性差。
统一export方式//封装的接口
functionobj1Func1(){}
functionobj1Func2(){}
functionobj2Func3(){}
functionobj2Func4(){}
//导出接口
export{
obj1Func1,//注释
obj1Func2,//注释
obj2Func3,//注释
obj2Func4,//注释
...
}
//引入方式
import{obj1Func1,obj1Func2,obj2Func3,obj2Func4}from'xxx'
//使用方式
constparams={...}
awaitobj1Func1(params)
这样加上函数功能注释,我们只需要在export内查找即可,比原来方便不少,但每次查找都需要拉到文件底下(export必须在函数定义之后),再一行行函数注释去看,还是不太方便。
面向对象的方式//module/objApi.js
functionobjFunc1(){}
functionobjFunc2(){}
functionobjFunc3(){}
functionobjFunc4(){}
//导出接口
exportdefault{
get:objFunc1,//注释
upd:objFunc2,//注释
del:objFunc3,//注释
add:objFunc4,//注释
...
}
//引入方式
importobjApifrom'module/objApi.js'
//使用方式
constparams={...}
awaitobjApi.get(params)
awaitobjApi.upd(params)
优点
面向对象,不需要每个接口函数都引入,开发时调用方便简化操作类型命名,如update - upd,开发和维护方便缺点
当模块涉及的对象很多,则需要建立非常多的文件,当文件名复杂时,难以看懂维护难度up。当页面需要引入多个对象,需要引入一个个文件,降低开发效率。找对象需要拖拽到文件最底部以面向模块(对象)的方式假设当前模块下涉及到 n 个对象及对应的增删改查接口, 定义一个映射表
//api映射表
constapiMap={
//公共接口
common:{
commonFun1,
commonFun2,
},
//对象1
dog:{
//增删改查
add:obj1Func1
get:obj1Func2
},
//对象2
cat:{
//增删改查
upd:obj2Func3,
del:obj2Func4,
},
...
}
?apiMap 对象
?「一级键名是模块涉及的对象
二级键名是对象相关的操作类型,值是对应的接口函数」
「导出方式1」直接导出各个对象
exportdefault{
commonApi:apiMap['common'],
dogApi:apiMap['dog'],
catApi:apiMap['cat'],
}
import{commonApi,...}from"xxx"
?面向模块(多个对象)
?本质就是以对象的方式来进行管理,只不过这里面向的是模块。这里一个模块只对应一个文件,包含了涉及到的n个对象的接口。因为我觉得「一个模块下建n个对象一长串的Api文件,又没法对文件名注释(文件名总有不认识或拼接的单词吧)只会带来更大的维护困难」
image.png找了几个不常见的英语名词,英语烂仔直接带上「痛苦面具」
「而有了映射表就相当于有了一个目录(文件最上方一目了然, Map下的对象十分清晰还有注释),」
至少目前我是能都秒读懂接口含义了
也不会出现面对老项目里那种长得拖不完的不知名接口文件的懵逼,点进去还只有几行代码(雪花飘飘~)
「优点:」
同上apiMap变成接口目录,可读性和可维护性提高(下方介绍)涉及同模块多个对象只需要引入一个文件「缺点:」
apiMap(目录)和export的位置一个在文件最上方,一个在最下方,浏览时非常不方便,依旧需要经常拖拽「这种方式已经比较好用了,从可维护性和可读性,拓展性来看我更推荐第二种方式」
「导出方式2」导出一个访问映射表的函数,参数是对象及操作类型,如(dog, upd)
//暴露一个访问api映射表的函数,参数是对象和操作
//这里没有错误处理,jym看懂就行
exportdefaultfunctionapi(obj,action){
if(action){
//返回某对象某操作的接口函数,如dogUpdate
returnapiMap[obj][action]
}
//返回一个包含多个操作接口函数的对象或公共接口
returnapiMap[obj]
}
//封装的接口
functionobj1Func1(){}
functionobj1Func2(){}
functionobj2Func3(){}
functionobj2Func4(){}
使用时方式1
importAPIfrom'xxx'
//没有错误处理,主打看懂
asyncfunctiongetData(){
constparams={...}
constdata=awaitAPI(obj1,'get')(params)
awaitAPI(obj1,'upd')(params)
awaitAPI(obj1,'del')(params)
}
使用时方式2
importAPIfrom'xxx'
constobj1API=API(obj1)
constdata=awaitobj1API.get(params)
constdata=awaitobj1API.upd(params)
constdata=awaitobj1API.del(params)
?api函数
?api函数可以复制或者写在公共模块引入就行了,实际上工作量只在维护映射表apiMap
现在查找接口原本一个模块里可能涉及10个对象共100个接口,顺序查找最差情况要看100条函数注释,而根据对象查找最差情况是10(对象)+10(操作类型)即20条函数注释。
constapiMap={
...
//注释:这是target对象
targetObj:{
add:objAddFunc,//注释:增删改查的话可有可无
upd:objUpdateFunc,
del:objDeleteFunc,
get:objGetFunc,
}
...
}
只需要关注目标对象(其实是注释),「清晰且一目了然,甚至不需要函数注释」,「不需要拖到文件底部」
可维护性和可读性模块化image.png这种方式用对象结构拆分也算是模块化了,看着不太习惯,但一个文件里对象和接口能都读懂,维护性和可读性也更好,即便接口函数再多行数再多,其实也只需要看apiMap
封装「(接下来开始胡扯~)」
api函数封装了一层,可以统一管理接口提高拓展性和复用性,例如统一给action为get的套一个节流函数
//暴露一个访问api映射表的函数,参数是对象和操作
//这里没有错误处理,jym看懂就行
exportdefaultfunctionapi(obj,action){
if(action){
if(action==="get"){
returnthrottle(apiMap[obj][action],500);//设置节流时间为500毫秒,期间返回空函数
}
returnapiMap[obj][action]
}
//返回一个包含多个操作接口函数的对象或公共接口
returnapiMap[obj]
}
或者当模块下有多个对象需要增删改查,且只需要一个参数id,那么只要再加个定制场景的api函数
//偏函数固定参数,这里constParams假设为{ id:007 }
exportfunctionparamsApi(constParams,obj){
//otherParams是额外参数,在各自接口做个合并Object.assign()
return(action,otherParams)=apiMap[obj][action](otherParams)
}
//固定参数
constconstParams={id:007}
constdogApi=paramsApi(constParams,'dog')
constcatApi=paramsApi(constParams,'cat')
//免参数直接调用即可
dogApi('get')
dogApi('update',{color:6})
dogApi('delete')
catApi('get')
catApi('update',{color:6})
catApi('delete')
这个场景有些理想化,但有一层封装也确实能够在需要时方便统一管理
然后这里 action: objActionFunc这里也算是一层封装,方便进行命名简写和规范
//xxxModule.js
constapiMap={
//对象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映射表
constapiMap={
...
}
//暴露api函数
exportdefaultapi
//封装的接口
...
开发时查找接口就全程对着文件最上方的映射表复制,基本「不需要怎么拖拽,不需要切换文件去找其他对象,也不需要看一堆无关代码,全程看注释,大大降低心智负担」,小白上手也能分分钟找到
// xxxModule.js
const apiMap = {
//对象1
dog: {
add: xxx
get: xxx
},
//对象2
cat: {
upd: xxx,
del: xxx,
},
}
引入接口时「只要一个API函数」,调用方式基本大差不差吧,反正都是copy,然后改下操作类型
importAPIfrom"@/api/xxx/xxxModule"
//调用时,两种方式,复制个对象名,记住个操作类型,完事
constdogAPI=API('dog')
constcatAPI=API('cat')
awaitdogAPI.get(params)
awaitdogAPI.upd(params)
awaitcatAPI.get(params)
//或
awaitAPI('dog','get')(params)
awaitAPI('dog','upd')(params)
awaitAPI('cat','get')(params)
开发效率我觉得和对象方式的API管理方案没啥区别,都是copy然后改下操作类型,「但其实最大的好处还是在可维护性和可读性上」
写在最后非常感谢看到最后的jym,这是我本0前端小白通过偏函数产生的一点小想法,感觉挺好用的分享一下(可能场景比较简单局限后台系统),欢迎jym多多指点发表建议(~玻璃心~)
如果文章对你有帮助的话欢迎「关注+点赞+收藏」
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线