记录--这个前端Api管理方案会更好?

news2024/11/27 21:00:13

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

简介

大家好,前端小白一枚,目前接触后台管理系统比较多,经常遇到不同对象的增删改查的接口,如何对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函数封装了一层,可以统一管理接口提高拓展性和复用性,例如统一给actionget的套一个节流函数

// 暴露一个访问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多多指点发表建议(玻璃心)

本文转载于:

https://juejin.cn/post/7290558277666930744

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1168951.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[概述] 点云滤波器

拓扑结构 点云是一种三维数据,有几种方法可以描述其空间结构,以利于展开搜索 https://blog.csdn.net/weixin_45824067/article/details/131317939 KD树 头文件:pcl/kdtree/kdtree_flann.h 函数:pcl::KdTreeFLANN 作用&#xff1a…

压缩软件 7-Zip VS WinZips?

7-zip在联想应用商店给强烈推荐? 要说它好用还行,但每次压缩都显示网络连接失败等异常广告信息。 相反好用的7-ZIP必须鼠标点击右键点击更多才能够看到,这次更新体验也太差了吧? 用户放在第一位? 要不是更新后一直推…

编译原理学习:随机生成算术表达式

最近用Python写了一个随机向右生成数学表达式的算法。如下图所示,点一下运行就能随机生成一个二叉树形式的算术表达式。这个树形图是用“graphviz”画的,完全是它自动布局画出来的,画的还挺不错的。代码在:becomequantum (becomeq…

Ubuntu 系统内核 kernel panic

Ubuntu 系统内核 kernel panic 不能进入系统:报错end kernel panic -not syncing: attemped to kill init! exit code 0x00000100 系统启动的时候,按下‘e’键进入grub编辑界面,编辑grub菜单,选择“kernel /vmlinuz-XXXXro root…

【LeetCode:117. 填充每个节点的下一个右侧节点指针 II | DFS | BFS】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

【强化学习】15 —— TRPO(Trust Region Policy Optimization)

文章目录 前言TRPO特点策略梯度的优化目标使用重要性采样忽略状态分布的差异约束策略的变化近似求解线性搜索算法伪代码广义优势估计代码实践离散动作空间连续动作空间 参考 前言 之前介绍的基于策略的方法包括策略梯度算法和 Actor-Critic 算法。这些方法虽然简单、直观&…

银行和金融企业为何青睐这8款项目管理工具

银行、金融行业中主流的8款项目管理系统:1.PingCode;2.Worktile;3.Microsoft Project;4.Jira by Atlassian;5.Asana;6.Trello;7.Wrike;8.Teambition。 银行和金融性质的公司在项目管…

【C++】多态 ⑪ ( 纯虚函数和抽象类 | 纯虚函数语法 | 抽象类和实现 | 代码示例 )

文章目录 一、纯虚函数和抽象类1、纯虚函数2、纯虚函数语法3、抽象类和实现 二、完整代码示例 一、纯虚函数和抽象类 1、纯虚函数 纯虚函数 : 在 C 语言中 , " 纯虚函数 " 是 特殊类型的 虚函数 , " 纯虚函数 " 在 父类 中 声明 , 但是没有实现 ; 抽象类 …

计算虚拟化2——内存虚拟化

目录 物理机内存访问过程 虚拟地址VA和物理地址PA概念 MUU实现VA到PA所使用的映射表 内存虚拟化类型 内存软件辅助虚拟化 内存硬件辅助虚拟化 内存虚拟化-内存超分配 内存共享 内存置换 内存气泡 物理机内存访问过程 内存的基本知识 内存都是从物理地址0开始的&…

基于LDA主题+协同过滤+矩阵分解算法的智能电影推荐系统——机器学习算法应用(含python、JavaScript工程源码)+MovieLens数据集(二)

目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据爬取及处理 相关其它博客工程源代码下载其它资料下载 前言 前段时间,博主分享过关于一篇使用协同过滤算法进行智能电影推荐系统的博文《基于TensorFlowCNN协同过滤算法的智能电影推荐系统——深…

文献查询辅助工具,查看文献影响因子期刊,显示文献排名,翻译文献

插件工具:easyScholar 适配浏览器(Edge、chrome、Firefox),本文以Edge为例: 1.打开Edge浏览器,输入: edge://extensions/ 2.点击获取Microsoft Edge扩展 3.搜索 easyscholar,然后…

Hive【Hive(八)自定义函数】

自定义函数用的最多的是单行函数&#xff0c;所以这里只介绍自定义单行函数。 Coding 导入依赖 <dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec</artifactId><version>3.1.3</version></dependency>…

路由器基础(十):防火墙配置

一、防火墙默认的区域 防火墙是基于安全区域进行工作的安全设备。 一个安全区域是若干接口所连网络的集合&#xff0c;这些网络中的用户具有相同的安全属性。 通常防火墙认为在同一安全区域内部发生的数据流动是不存在安全风险的&#xff0c;不需要实施任何安全策略。只有当不同…

代码随想录Day36 动态规划05 LeetCode T1049最后一块石头的重量II T494 目标和 T474 一和零

前言 : 动规五部曲 理论基础 : 代码随想录Day34 LeetCode T343整数拆分 T96 不同的二叉搜索树-CSDN博客 1.明白dp数组的含义 2.明白递推公式的含义 3.初始化dp数组 4.注意dp数组的遍历顺序 5.打印dp数组排错 LeetCode T1049 最后一块石头的重量II 题目链接:1049. 最后一块石头…

LeetCode:117. 填充每个节点的下一个右侧节点指针 II(C++)

117. 填充每个节点的下一个右侧节点指针 II 题目描述&#xff1a; 给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将…

Promise的并发控制 - 从普通并发池到动态并发池

一、场景 给你一个有200个URL的数组&#xff0c;通过这些URL来发送请求&#xff0c;要求并发请求数不能超过五个。 这是一道很常考的面试题&#xff0c;接下来让我们来学习一下Promise并发控制 二、普通并发池的实现 主要思路就是&#xff0c;判断当前队列是否满&#xff0c;…

【IDEA】在工具栏设置快速创建包和类的图表

页面效果&#xff1a; 操作步骤&#xff1a; 设置 --> 外观与行为 --> 菜单与工具栏 --> 点击 主工具栏 --> 点击 ---- --> 点击 号 --> 添加操作 主菜单 --> 文件 --> 文件打开操作 --> 打开项目操作 --> 新建 --> 往下找 找到 clas…

单行自动横向滚动——css实现

效果 封装组件 <template><div ref"container" class"scroll-area"><divref"content":class"[isScroll ? scroll : no-scroll]":style"{ color: fontColor }">{{ content }}</div></div> &…

c++值deque容器

1.deque容器介绍 deque 是 double-ended queue 的缩写&#xff0c;又称双端队列容器。deque容器支持从头部和尾部双端插入、删除数据。与vector容器不同的是&#xff0c;vector容器是一段连续的空间&#xff0c;而deque没有所谓容量的概念&#xff0c;因为它是动态的以分段连续…

Spring Boot 常见面试题

目录 1.Spring Boot 快速入门什么是 Spring Boot&#xff1f;有什么优点&#xff1f;Spring Boot 与 Spring MVC 有什么区别&#xff1f;Spring 与 Spring Boot 有什么关系&#xff1f;✨什么是 Spring Boot Starters?Spring Boot 支持哪些内嵌 Servlet 容器&#xff1f;如何设…