axios接口管理优化操作详解
强化功能
本文针对中大型的后台项目的接口模块优化,在不影响项目正常运行的前提下,增量更新。
- 接口文件写法简化(接口模块半自动化生成)
- 任务调度、Loading调度(接口层面的防抖兜底,多个接口共用一个loading,防止闪烁)
- 接口提示自由化(提示消息可由前端控制,也可以由后端控制)
接口文件写法简化
对于一些中后台模块的接口,基本上都是增删改查以及审核流的一些功能(其他特殊接口暂且不谈)。如果后端接口足够规范的话,大概就是下面这个情形
import request from "@/utils/request";
// 销售退货列表
export function getSalesReturnList(data) {
return request({
url: "/sales_return/list",
method: "post",
data,
});
}
// 保存销售退货
export function saveSalesReturn(data) {
return request({
url: "/sales_return/save",
method: "post",
data,
});
}
// 根据Id获取销售退货
export function getSalesReturn(query) {
return request({
url: "/sales_return/get",
method: "get",
params: query,
});
}
// 根据Id删除销售退货
export function deleteSalesReturn(data) {
return request({
url: "/sales_return/delete",
method: "post",
data,
});
}
// 提交销售退货审核
export function submitSalesReturn(data) {
return request({
url: "/sales_return/submit",
method: "post",
data,
});
}
// 审核销售退货
export function auditSalesReturn(data) {
return request({
url: "/sales_return/audit",
method: "post",
data,
});
}
// 撤审销售退货
export function revokeAuditSalesReturn(data) {
return request({
url: "/sales_return/withdraw",
method: "post",
data,
});
}
// 审核拒绝销售退货
export function rejectSalesReturn(data) {
return request({
url: "/sales_return/reject",
method: "post",
data,
});
}
// 作废销售退货
export function discardSalesReturn(data) {
return request({
url: "/sales_return/discard",
method: "post",
data,
});
}
我觉得这个也太重复了,而且接口函数命名太麻烦了,要让团队规范起来比较困难。能不能自动生成了,命名也帮忙处理了,这样这种接口文件岂不是更加规范。
接下来想想办法
假设如上,一个单据模块的通常来说有九个接口方法,增删改查,提交、作废、审核、撤审、拒绝。他们的 url,前面的 sales_return 拼接是固定的,不同的就是后面标识功能的路径标识。另外就是,method 分为 post 和 get 方法。
我们把这九个接口,看成是一个 9 位二进制上的 9 个位,1 代表存在,0 代表不存在。
我们可以创建一个 map 文件来做构建准备(如下)
export const apiEnum = {
// 查列表 2^0
1: {
name: "list",//接口名称
type: "post",//接口方式
},
// 查详情 2^1
2: {
name: "get",
type: "get",
loading: true,//是否需要loading调度、防抖
},
// 删列表 2^2
4: {
name: "delete",
type: "post",
},
// 保存 或者 保存且提交 2^3
8: {
name: "save",
type: "post",
loading: true,
},
// 提交 2^4
16: {
name: "submit",
type: "post",
loading: true,
},
// 审核 2^5
32: {
name: "audit",
type: "post",
},
// 撤审 2^6
64: {
name: "withdraw",
type: "post",
},
// 拒绝 2^7
128: {
name: "reject",
type: "post",
},
// 作废 2^7
256: {
name: "discard",
type: "post",
},
};
export const apiFuncModule = {
// 全部
COMMON: 511,
// 增删改查
CURD: 15,
};
当我传 1 的时候,九位为000000001
,代表只有一个查接口。当我传 15 的时候,九位为000001111
,代表拥有增删改查四个接口。以此类推。
接下就是完成处理函数,完成上面的功能(如下)
import request from "@/utils/request";
import { apiEnum, apiFuncModule } from "@/enum/baseModule/apiEnum";
function useApi(moduleName, code = 511) {
let apiMap = {};
for (let key in apiEnum) {
if ((key & code) == key) {
let obj = apiEnum[key];
//可以按自己习惯来对接口函数命名
let apiName = "api_" + obj.name;
apiMap[apiName] = (data) => {
return request({
url: `/${moduleName}/${obj.name}`,
method: obj.type,
[obj.type == "get" ? "params" : "data"]: data,
loading: obj.loading,
});
};
}
}
return apiMap;
}
export { useApi, apiFuncModule as apiType };
完成以上步骤,我们的接口文件就可以这样写了,这样九个接口就写完了。而且一目了然,如需修改,只需要调整传参就行了。
import { useApi } from "@/utils/system/apiGenPlugin";
//code可以不传 ,默认为511
export const API = useApi("sales_return");
//若有其他特殊接口 兼容原始写法 互不影响
export function xxxxx(data) {
...
}
使用方式
//API集中管理
import { API as SalesReturn } from "@/api/workApi/sale/return";
const {api_save,api_delete,api_get,api_list,api_audit,api_withdraw,api_discard,api_submit,api_reject} = SalesReturn
//单独使用
import { useApi } from "@/utils/system/apiGenPlugin";
const {api_save,api_delete,api_get,api_list,api_audit,api_withdraw,api_discard,api_submit,api_reject} = useApi('sales_return')
- 增 SalesReturn.api_save
- 删 SalesReturn.api_delete
- 改 SalesReturn.api_get
- 查 SalesReturn.api_list
- 审核 SalesReturn.api_audit
- 撤审 SalesReturn.api_withdraw
- 作废 SalesReturn.api_discard
- 提交 SalesReturn.api_submit
- 拒绝 SalesReturn.api_reject
任务调度、Loading调度
实际开发中,我们可能会有对接口调用做一些处理
- 对提交事件进行防抖处理,防止重复提交。
- 加载某些重要资源的时候,希望有个loading效果,来优化用户体验。
- 让多个需要loading效果的接口,共用同一个loading,防止页面闪烁。
这些功能单独处理起来就显得很麻烦了,而且每个人的写法不一样,后期维护成本就更难。
废话不多说,直接贴代码
接口调度类
import { Loading } from "element-ui";
class RequestLock {
// Loading 实例
L_instance = null;
// 接口map
reqMap = new Map();
// 最近一次调用接口时间戳
timestamp = 0;
constructor(timeout = 500) {
// 过渡时间
this.timeout = timeout;
}
// 创建任务
put = (id) => {
if (this.reqMap.has(id)) return false;
this._put(id);
return true;
};
_put = (id) => {
this.timestamp = new Date().getTime();
this.reqMap.set(id, true);
//开启loading
this.L_instance = Loading.service({
fullscreen: true,
background: "rgba(255, 255, 255, 0.1)",
lock: true,
});
};
// 移除任务
del = (id) => {
if (this.reqMap.has(id)) {
this.reqMap.delete(id);
if (this.reqMap.size == 0) {
this._closeLoading();
}
}
};
// 清空所有的任务
clearTask = () => {
this.reqMap.clear();
this.L_instance.close();
};
//平滑关闭loading
_closeLoading = () => {
let _timestamp = new Date().getTime();
let settime = _timestamp - this.timestamp;
if (settime > this.timeout) {
this.L_instance?.close();
} else {
setTimeout(() => {
this.L_instance?.close();
}, this.timeout - settime);
}
};
}
export default RequestLock;
在axios里的使用
这个是增量优化,在不影响以前代码的条件下,添加功能
import { RequestLock } from "@/class/lock";
let loadLock = new RequestLock(500);
//请求拦截
service.interceptors.request.use(
(config) => {
...
//如果配置中有loading 开启调度
if (config.loading) {
if (!loadLock.put(config.url)) {
return Promise.reject(new Error("repeat request!"));
}
}
...
return config;
},
(error) => {
...
//如果有错误请求,中止当前调度任务,并清空
loadLock.clearTask();
...
return Promise.reject(error);
}
);
//响应拦截
service.interceptors.response.use(
(response) => {
...
//检查
response.config.loading && loadLock.del(response.config.url);
...
},
(error) => {
loadLock.clearTask();
return Promise.reject(error);
}
);
接口文件书写
// 根据Id获取销售退货
export function getSalesReturn(query) {
return request({
url: "/sales_return/get",
method: "get",
params: query,
//在这里配置loading为true,开启
loading:true
});
}
提示信息自由化
有时候当我删除一条数据,需要有个弹框提示删除是否成功。通常我们会在接口成功回调的时候加上这个功能。需要判断状态,来显示提示框的描述和颜色。另一方面,有时候删除一条数据,业务需求提示不单单是简单的“删除成功!”,还可能需要其他的附加提示。比如“删除单据xxx成功,请及时处理xxxx!”。这种需求没什么难度,但是有沟通成本和维护成本。业务有一些变化就需要修改。
另一方面,后端对系统的业务逻辑更加贴近,提示功能交给后端更加合理。当然,前端也需要保留这个功能,去兼容某些需求。
import { Message } from "element-ui";
export function responseMsgHandle(res) {
//这里需要后端响应数据格式的配合,MsgType表示提示状态,Msg表示提示描述
let { MsgType, Msg } = res;
if (["success", "warning", "error"].includes(MsgType)) {
Message({
message: Msg,
type: MsgType,
duration: 5 * 1000,
});
}
}
使用
import { responseMsgHandle } from "@/utils";
//响应拦截
service.interceptors.response.use(
(response) => {
...
const res = response.data;
responseMsgHandle(res);
...
},
(error) => {
...
responseMsgHandle({
MsgType:"error",
Msg:error.message,
});
...
return Promise.reject(error);
}
);
总结
以上三个简单的优化方案,可以组合使用,也可以单独使用。可以根据自己的实际项目需求,进行改造使用。
基本上能解决很大一部分的重复劳动,还能减少维护成本。
以上就是axios接口管理优化操作详解的详细内容,更多关于axios 接口管理的资料请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341