Optimize the Sao operation based on axios interface management

Optimize the cool operation based on axios interface management!

This article is aimed at optimizing the interface modules of medium and large backend projects and updating them incrementally without affecting the normal operation of the project.

Enhanced functionality

1. Simplified writing of interface files (semi-automatic generation of interface modules)
2. Task scheduling and loading scheduling (anti-shake at the interface level, multiple interfaces share one loading to prevent flickering)
3. Free interface prompts (prompt messages can be controlled by the front end) , can also be controlled by the backend)

Simplified interface file writing method

For the interfaces of some mid- and back-end modules, they are basically functions of adding, deleting, modifying, checking, and auditing flows (other special interfaces will not be discussed for the time being). If the back-end interface is standardized enough, it will probably be the following situation

 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,});
} 

I think this is too repetitive, and the interface function naming is too troublesome, and it is difficult for the team to standardize it. Can it be automatically generated and the naming can be helped, so that this kind of interface file will be more standardized.

Think of a way next

Assuming the above, a document module generally has nine interface methods, add, delete, modify, check, submit, void, review, withdraw review, and reject. For their URLs, the sales_return splicing in the front is fixed. The difference is the path identifier that identifies the function in the back. In addition, the method is divided into post and get methods.

We regard these nine interfaces as 9 bits on a 9-bit binary number. 1 represents existence and 0 represents non-existence.

We can create a map file to prepare for the build (as shown below)

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,
}; 

When I pass 1, the nine bits are 000000001, which means there is only one query interface. When I pass 15, the nine digits 000001111represent four interfaces: add, delete, modify, and check. And so on.

The next step is to complete the processing function and complete the above function (as follows)

 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 }; 

After completing the above steps, our interface file can be written like this, so that the nine interfaces have been written. And it is clear at a glance. If you need to modify it, you only need to adjust the parameters.

import { useApi } from "@/utils/system/apiGenPlugin";
//code可以不传 ,默认为511
export const API = useApi("sales_return");
//若有其他特殊接口 兼容原始写法 互不影响
export function xxxxx(data) {...
} 

Usage

 //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') 
  • Add SalesReturn.api_save
  • DeleteSalesReturn.api_delete
  • 改 SalesReturn.api_get
  • Check SalesReturn.api_list
  • Audit SalesReturn.api_audit
  • Withdraw SalesReturn.api_withdraw
  • Discard SalesReturn.api_discard
  • SubmitSalesReturn.api_submit
  • Reject SalesReturn.api_reject

Task scheduling, Loading scheduling

In actual development, we may do some processing on interface calls

1. Perform anti-shake processing on submission events to prevent repeated submissions.
2. When loading certain important resources, I hope there will be a loading effect to optimize the user experience.
3. Let multiple interfaces that require loading effects share the same loading to prevent page flickering.

These functions are cumbersome to handle individually, and everyone's writing style is different, making later maintenance costs even more difficult.

Without further ado, just post the code.

Interface scheduling class

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;
​ 

Use in axios

This is incremental optimization, adding functionality without affecting previous code.

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);}
); 

Interface file writing

// 根据Id获取销售退货
export function getSalesReturn(query) {
  return request({
    url: "/sales_return/get",
    method: "get",
    params: query,
    //在这里配置loading为true,开启
    loading:true});
} 

Tips for information liberalization

Sometimes when I delete a piece of data, I need a pop-up box to indicate whether the deletion was successful. Usually we add this function when the interface successfully calls back. The status needs to be determined to display the description and color of the prompt box. On the other hand, sometimes when a piece of data is deleted, the business requirement prompt is not just a simple "Delete successfully!", but may also require other additional prompts. For example, "Delete document xxx successfully, please process xxxx in time!". This requirement is not difficult, but there are communication costs and maintenance costs. Modifications are needed if there are some changes in the business.

On the other hand, the backend is closer to the business logic of the system, and it is more reasonable to leave the prompt function to the backend. Of course, the front end also needs to retain this function to be compatible with certain needs.

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,
  });}
} 

use

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);}
); 

Summarize

The above three simple optimization solutions can be used in combination or individually. It can be modified and used according to your actual project needs.

Basically it can solve a large part of the repetitive work and reduce maintenance costs.

at last

Recently I found a VUE document that summarizes various knowledge points of VUE and compiled it into "36 Tips You Must Know for Vue Development". The content is relatively detailed, and the explanations of various knowledge points are also very good.



Friends in need can click on the card below to receive it and share it for free

Guess you like

Origin blog.csdn.net/web2022050901/article/details/129270030