经验总结:typescript 和 axios 项目中大量接口该怎样管理和组织

[复制链接]
发表于 2024-11-2 17:09:49 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
引言

本文旨在先容一种方法,用于在 typescript 和 axios 的项目中,有效的组合和管理大量的 API 接口以及 interface。
假如我们根据 API 文档对全部的接口做了初步分类,大体如下:
  1. scm(某业务模块)
  2. ├── inventory(库存业务)
  3. │    ├── warehouse # 仓库资源
  4. │    ├── material  # 物料资源
  5. │    ├── unit      # 计量单位资源
  6. │    └── ...
  7. ├── order(订单业务)
  8. │     ├── order    # 订单资源
  9. │     ├── customer # 客户资源
  10. │     └── ...
  11. └── ...
复制代码
具体来说,对于单个但资源后端提供了一系列 CRUD 操作接口, 以warehouse为例:
类型方法&路径列表<GET /api/scm/warehouse/>创建<OST /api/scm/warehouse/>详情<GET /api/scm/warehouse/1/>整体更新<UT /api/scm/warehouse/1/>部分更新<ATCH /api/scm/warehouse/1/>删除<DELETE /api/scm/warehouse/1/> 在项目中,我们会有大量的资源,每种资源又会产生很多接口,对接这些接口我们必要创建很多请求函数,因此怎样管理这些请求函数变得尤为重要。本文将先容一种高效的组织方法,话不多说,让我们开始吧。
一、封装 axios

我们不讨论对axios的深度封装,这里只做最简朴的处理:
  1. // utils/request.ts
  2. import { useTokenStore } from "@/stores/auth/token";
  3. import axios from "axios";
  4. // 后端API统一的返回结构
  5. export type Result<T> = {
  6.   status: string;
  7.   code: number;
  8.   message: string[];
  9.   result: T;
  10. };
  11. const tokenStore = useTokenStore();
  12. const access = tokenStore.getToken.access;
  13. // 配置新建一个 axios 实例
  14. const instance = axios.create({
  15.   baseURL: import.meta.env.VITE_API_URL || "http://localhost:8000",
  16.   timeout: 60000,
  17. });
  18. // 添加请求拦截器
  19. instance.interceptors.request.use(
  20.   function (config) {
  21.     // 添加token
  22.     if (access) {
  23.       config.headers.Authorization = `Bearer ${access}`;
  24.     }
  25.     return config;
  26.   },
  27.   function (error) {
  28.     // 对请求错误做些什么
  29.     return Promise.reject(error);
  30.   }
  31. );
  32. // 添加响应拦截器
  33. instance.interceptors.response.use(
  34.   function (response) {
  35.     // 2xx 范围内的状态码都会触发该函数。
  36.     // 对响应数据做点什么
  37.     return response;
  38.   },
  39.   function (error) {
  40.     // 超出 2xx 范围的状态码都会触发该函数。
  41.     // 对响应错误做点什么
  42.     return Promise.reject(error);
  43.   }
  44. );
  45. export default instance;
复制代码
二、对接接口

在引言中我们先容了全部的资源接口,以及其分类:
  1. scm(某业务模块)
  2. ├── inventory(库存业务)
  3. │    ├── warehouse # 仓库资源
  4. │    ├── material  # 物料资源
  5. │    ├── unit      # 计量单位资源
  6. │    └── ...
  7. ├── order(订单业务)
  8. │     ├── order    # 订单资源
  9. │     ├── customer # 客户资源
  10. │     └── ...
  11. └── ...
复制代码
根据分类,我们创建下面的目录接口来存放请求函数:
  1. /vue-project
  2. ├── /src
  3. │   └── /api
  4. │        ├── ...
  5. │        └── /scm
  6. │            ├── inventory.ts  (示例所在)
  7. │            ├── order.ts
  8. │            ├── ...
  9. │            └── index.ts
复制代码
以warehouse资源为例,我们要为其全部的资源操作接口创建请求方法、要为其全部接口的请求体和相应体创建interface:
  1. // .../scm/inventory.ts
  2. import request, { type Result } from "@/utils/request";
  3. const baseUrl = "/api/scm";
  4. // 仓库接口
  5. // ----------------------------------------------------
  6. export interface WarehouseBaseIn {
  7.   name: string;
  8.   code: string;
  9. }
  10. export interface WarehouseBaseOut {
  11.   id: number;
  12.   name: string;
  13.   code: string;
  14.   created_by: number;
  15.   created_at: string;
  16. }
  17. export interface WarehousePatchIn {
  18.   name?: string;
  19.   code?: string;
  20. }
  21. export const warehouse = {
  22.   list: () =>
  23.     // 获取列表
  24.     request<Result<WarehouseBaseOut[]>>({
  25.       url: baseUrl + "/warehouse/",
  26.       method: "GET",
  27.     }),
  28.   create: (data: WarehouseBaseIn) =>
  29.     // 创建
  30.     request<Result<WarehouseBaseOut[]>>({
  31.       url: baseUrl + "/warehouse/",
  32.       method: "POST",
  33.       data,
  34.     }),
  35.   retrieve: (id: string) =>
  36.     // 按ID查询
  37.     request<Result<WarehouseBaseOut[]>>({
  38.       url: baseUrl + `/warehouse/${id}/`,
  39.       method: "GET",
  40.     }),
  41.   put: (id: string, data: WarehouseBaseIn) =>
  42.     // 全部更新
  43.     request<Result<WarehouseBaseOut[]>>({
  44.       url: baseUrl + `/warehouse/${id}/`,
  45.       method: "PUT",
  46.       data,
  47.     }),
  48.   patch: (id: string, data: WarehousePatchIn) =>
  49.     // 部分更新
  50.     request<Result<WarehouseBaseOut[]>>({
  51.       url: baseUrl + `/warehouse/${id}/`,
  52.       method: "PATCH",
  53.       data,
  54.     }),
  55.   destroy: (id: string) =>
  56.     // 删除
  57.     request<Result<any>>({
  58.       url: baseUrl + `/warehouse/${id}/`,
  59.       method: "Delete",
  60.     }),
  61. };
  62. // 除了warehouse之外,inventory文件中实际上还会有更多的接口。
  63. // 比如: material、unit等等,封装的方法都是类似的。
  64. // ...
复制代码
对单个资源的封装可以总结为如下布局:
  1. const source = {
  2.     list: function...,
  3.     create: function...,
  4.     retrieve: function...,
  5.     put: function...,
  6.     patch: function...,
  7.     destroy: function...
  8. }
复制代码
回首我们的目录布局:
  1. /vue-project
  2. ├── /src
  3. │   └── /api
  4. │        ├── ...
  5. │        └── /scm
  6. │            ├── inventory.ts   (示例所在)
  7. │            ├── order.ts
  8. │            ├── ...
  9. │            └── index.ts
复制代码
上述的目录布局,使得我们可以对大量的资源举行分类组织,而为了方便请求函数调用,我们还必要在index.ts中对请求函数举行汇总。
  1. // .../scm/index.ts
  2. import { warehouse, material, unit,... } from './inventory'
  3. import { order, customer, ... } from './order'
  4. const api = {
  5.   inventory: {
  6.     warehouse,
  7.     material,
  8.     unit,
  9.     ...
  10.   },
  11.   order: {
  12.     order,
  13.     customer,
  14.     ...
  15.   }
  16. }
  17. export default api
复制代码
使用方法如下,并且可以或许充分得到 ide 提示:
  1. import api from "@/api/scm";
  2. // 使用.时ide的提示会很友好
  3. api.inventory.warehouse.list();
  4. api.inventory.material.list();
  5. api.order.customer.list();
  6. api.order.order.list();
复制代码
总结

对资源接口做好分类,将请求函数组织在合理的目录布局中,有助于进步代码的可维护性和可扩展性。同时对请求函数举行统一封装和组织,使得对请求的调用更加方便。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表