Axios真企业级项目封装实践

287 阅读7分钟

真企业级Axios项目封装实践

1.前言:

随着前端框架不断发展,项目规模往往快速增大,显而易见的在于所发送与维护的接请求种类与数量不断增多,而在一个项目中,多数的请求所需要的配置往往是一样的,如果每发起一次请求,就要把这些比如设置错误处理设置请求头请求地址选择映射匹配等等操作,都重新写一个,这样不仅浪费时间而且使得代码非常冗余与臃肿。所以在今天的大型项目中必须要进行Axios或其他网络请求工具的封装,二次封装具备以下多种好处:

  • 统一接口管理: 大型项目通常会有多个模块、多个后端接口,直接使用Axios来发送请求可能会导致代码冗余和混乱。通过对Axios进行二次封装,可以将接口统一管理,使代码更加清晰和可维护。
  • 抽象请求逻辑: 大型项目中的请求逻辑可能包括统一的请求头、请求拦截、响应拦截、错误处理等。通过二次封装Axios,可以将这些公共的请求逻辑抽象出来,避免在每个请求中重复编写这些逻辑,提高代码的复用性。
  • 定制化配置: 在大型项目中,对请求的配置可能会有一些特定的需求,例如设置超时时间、设置请求的baseURL、设置认证信息等。通过二次封装Axios,可以在封装的过程中根据项目需求进行定制化配置,提高灵活性和可定制性。
  • 错误处理和统一提示: 在大型项目中,错误处理和统一提示是非常重要的一环。通过二次封装Axios,可以统一处理请求错误,例如网络错误、接口返回错误码等,并进行适当的提示,提高用户体验和系统的健壮性。
  • 接口模块化和扩展性: 通过二次封装Axios,可以将接口按照模块进行划分和管理,提高代码的可读性和可维护性。同时,在项目后期的功能扩展中,也可以通过扩展封装的Axios实现接口的快速迭代和调整。

今天,本博客会带来一套真正的企业级Axios项目封装实践,真正实现一次封装,多处收益

注意,如果还对Axios这个工具不太熟悉的同学可以到Axios官网 深入学习一下axios,很快就能上手的!

2.项目准备与代码结构

所需要的相关依赖:

 npm install axios --save
 yarn add axios --save
 pnpm add axios --save
 # 其他下载依赖方式均可

整体的项目结构:

 项目结构:
 |-- api
 |   |-- community.ts  # 社区相关接口
 |   |-- user.ts     # 用户相关接口
 |   |-- shop.ts     # 商店相关接口
 |   |-- requset.ts     # 请求核心配置文件
 |-- tools
 |   |-- auth.ts     # 权限相关工具文件
 |-- constants
 |   |-- responseCode.ts  # 接口响应相关常量
 |-- types
 |   |-- request.d.ts  # 请求所涉及自定义类型的集中维护
 ​

3.Axios配置

3.1 工具、常量、类型的配置

Auth工具的配置

在Auth.ts文件中编写以下代码

 /**
  * 设置token
  * @param token
  * @returns
  */
 export const setToken = (token: string) =>
     sessionStorage.setItem("token", token);
 ​
 /**
  * 获取token
  * @returns
  */
 export const getToken = () => window.localStorage.getItem("token");
 ​
 /**
  * 获取token
  * @returns
  */
 export const clearToken = () => window.localStorage.removeItem("token");
 ​

Response常量的配置

在responseCode.ts文件中编写以下代码

 export const ERROR_CODE: { [key: number]: string } = {
     400: "请求失败",
     401: "无权访问",
     403: "紧张访问",
     404: "请求不存在",
     405: "请求方法错误",
     406: "请求的格式错误",
     410: "资源已删除",
     422: "验证错误",
     500: "服务器发生错误",
     502: "网关错误",
     503: "服务器暂时过载或维护",
     504: "网关超时",
 };
 ​
 export const TOKEN_ERROR_CODE: { [key: number]: string } = {
     506: "请先登录",
     507: "请重新登录",
     508: "登录已过期",
 };
 ​

request请求类型的配置

为使得本实践更利于维护,采用ts进行,所以采用统一维护请求相关类型信息,在request.d.ts文件中编写以下代码

 export interface ResponseDataType {
     code: number;
     message: string;
     data: any;
 }
 ​
 export interface requestConfigType {
     headers?: { [key: string]: string };
     onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;
     signal?: GenericAbortSignal;
     hold?: boolean;
     timeout?: number;
     isToken?: boolean;
 }
 ​

3.2 Axios的基础信息配置

本配置的代码均在request.ts核心配置文件中编写,代码里面都有详细的注释,结合注释对于以了解axios的新手也完全可以看懂

 import axios, {
     Method,
     InternalAxiosRequestConfig,
     AxiosResponse,
     GenericAbortSignal,
     AxiosProgressEvent,
 } from "axios";
 import NProgress from "nprogress";
 import "nprogress/nprogress.css";
 import { clearToken, getToken } from "../tools/auth";
 import { TOKEN_ERROR_CODE } from "../constants/responseCode";
 import { ResponseDataType, requestConfigType } from "../types/request";
 ​
 const instance = axios.create({
     baseURL: "/api", // 开发环境下的跨域解决
     timeout: 5000, //配置超时时间
     withCredentials: true, //携带凭证允许
     headers: {
         "content-type": "application/json",
     }, //请求头格式
 });
 ​

3.3 Axios拦截器配置

本配置的代码均在request.ts核心配置文件中编写,代码里面都有详细的注释,结合注释对于以了解axios的新手也完全可以看懂

 /* 请求拦截 */
 instance.interceptors.request.use(requestAuth, (err) => Promise.reject(err));
 ​
 /* 响应拦截 */
 instance.interceptors.response.use(responseSuccess, (err) => {
     NProgress.done(); // 关闭loading
     return Promise.reject(err);
 });
 ​
 ​
 ​
 /**
  * 请求开始前的检查
  */
 function requestAuth(
     config: InternalAxiosRequestConfig & requestConfigType
 ): InternalAxiosRequestConfig {
     if (config.headers && config.isToken) {
         console.log("auth-consig-token");
         config.headers.Authorization = `Bearer ${getToken()}`;
         // 需要使用token的请求
     }
     // 不需要使用token的请求,直接送出即可
     NProgress.start(); // 启动loading
     return config;
 }
 ​
 /**
  * 请求成功,检查请求头
  */
 function responseSuccess(response: AxiosResponse<ResponseDataType>) {
     NProgress.done(); // 关闭loading
     return response;
 }
 ​
 ​

3.4 Axios请求统一整合

本配置的代码均在request.ts核心配置文件中编写,代码里面都有详细的注释,结合注释对于以了解axios的新手也完全可以看懂

 function request(url: string, data: any, config: any, method: Method): any {
     /* 去空 */
     for (const key in data) {
         if (data[key] === null || data[key] === undefined) {
             delete data[key];
         }
     }
 ​
     return instance
         .request({
             url: url,
             method: method,
             data: method === "GET" ? null : data,
             params: method === "GET" ? data : null, // get请求不携带data,params放在url上
             ...config, // 用户自定义配置,可以覆盖前面的配置
         })
         .then((res) => checkRes(res.data)) //这里将数据data解构出来了,直接放到下一步checkRes中去了
         .catch((err) => responseError(err));
 }
 ​

3.5 分类型暴露

本配置的代码均在request.ts核心配置文件中编写,代码里面都有详细的注释,结合注释对于以了解axios的新手也完全可以看懂

 /**
  * api请求方式
  * @param {String} url
  * @param {Any} params
  * @param {Object} config
  * @returns
  */
 export function GET<T>(url: string, params = {}, config: any = {}): Promise<T> {
     return request(url, params, config, "GET");
 }
 ​
 export function POST<T>(url: string, data = {}, config: any = {}): Promise<T> {
     return request(url, data, config, "POST");
 }
 ​
 export function PUT<T>(url: string, data = {}, config: any = {}): Promise<T> {
     return request(url, data, config, "PUT");
 }
 ​
 export function DELETE<T>(
     url: string,
     data = {},
     config: any = {}
 ): Promise<T> {
     return request(url, data, config, "DELETE");
 }
 ​

4.接口封装

分类型或模块的划分接口,对接口进行封装与暴露,例如一个项目划分成 用户相关接口/社区相关接口等等

社区相关接口的一个案例:

 /**
  * 社区相关接口信息
  *
  * 查询社区列表
  * 获取所有社区列表
  * 获取用户所在社区详细信息
  * xxxxxx
  *
  */
 import { GET, POST } from "./request";
 ​
 /**
  * 查询社区列表
  * @param data
  * @returns
  */
 export const searchCommunityList = (data: { id: string; location: string }) => {
     console.log(data);
 ​
     return GET("/community/search", data, { isToken: true });
 };
 ​
 /**
  * 获取所有社区列表
  * @param {}
  * @returns
  */
 export const getAllCommunityList = () =>
     GET("/community/all", {}, { isToken: true });
 ​
 /**
  * 获取用户所在社区详细信息
  * @param {}
  * @returns
  */
 export const getCommunityDetailByToken = () =>
     GET("/community/detail/self", {}, { isToken: true });
 ​

5.组件内调用

这样子,只需要最后一步就是组件内部调用相关的接口封装,我们只需要调用相关的接口函数接口,而且这样封装的函数是非常有利于async await这样的调用风格的

社区相关组件的一个案例:

 import { useState } from "react";
 ​
 const data = [
     {
       title: "Ant Design Title 1",
     },
     {
       title: "Ant Design Title 2",
     },
     {
       title: "Ant Design Title 3",
     },
     {
       title: "Ant Design Title 4",
     },
   ];
 ​
   
   
     const [communityList, setCommunityList] = useState<Array<object>>(data);
     const handleGetAllCommunityList = async () => {
         try {
             const data: Array<object> = await getAllCommunityList();
             message.success("获取所有社区成功!");
             setCommunityList(data);
         } catch (error) {
             console.log(error);
         } finally {
             console.log("请求结束");
         }
     };
 ​
     const handleSearchCommunityList = async () => {
         try {
             const data: Array<object> = await searchCommunityList({
                 id: "2222",
                 location: "北京",
             });
             message.success("搜索社区成功!");
             setCommunityList(data);
         } catch (error) {
             console.log(error);
         } finally {
             console.log("请求结束");
         }
     };

6. 总结

本实践没有过多的文本描述,多在代码中的注释。但通过此个实践了解学习之后,应该可以较好的掌握这套企业级的Axios请求封装解决方案,能够真正实现一次封装,多处收益

相关的配套实践Demo会上传Github开源

项目链接:Axios企业级封装实践