vben 之 axios 封装

vben 之 axios 封装

axios 封装,这是一个从新手入门就要开始做的一件事情,现在让我们看一下 vben 中是如何实现 axios 的封装的。

vben 中 axios 的封装

vben 中的 axios 封装的代码在packages\effects\request\src\request-client\request-client.ts

我们简单介绍一下这个封装,首先,vben 使用class,然后类中有一些属性

ts 复制代码
  // 方法: 添加请求拦截器 添加响应拦截器
  public addRequestInterceptor;
  public addResponseInterceptor;

  // 方法:下载   // 方法:上传
  public download ;
  public upload ;

  // 是否正在刷新token
  public isRefreshing = false;
  // 刷新token队列
  public refreshTokenQueue: ((token: string) => void)[] = [];

  // axios实例
  private readonly instance: AxiosInstance;

  /** POST请求方法 */
  public post<T = any>(url: string, data?: any, config?: RequestClientConfig): Promise<T> { }

  /** 通用的请求方法 */
  public async request<T>(url: string, config: RequestClientConfig): Promise<T> {}
addRequestInterceptoraddResponseInterceptor

这两个特别简单,实际上就像下面代码一样,为了写起来简单和语义化,进行了一层封装。

ts 复制代码
addRequestInterceptor(fulfilled,rejected){
  this.instance.interceptors.request.use(fulfilled, rejected);
}
uploaddownload

这个也很简单。

  1. 文件上传时,请求参数为 formData 的格式,而upload正是将json格式的请求参数转为 formData 格式,同时设置请求头的Content-Typemultipart/form-data
  2. 文件下载的封装也很简单,就是在请求头中,增加了一个responseTypeblob,这样,响应的数据,会自动转为 blob。如果要下载文件,则将 blob 转为 blob 的 url,并使用 a 标签进下载。
  3. 然而,这种做法仅适合小文件下载,因为 响应的数据 blob 会存在浏览器的内存中,此时,浏览器占用内存会变大,影响到浏览器性能。正确方法应该是后端返回文件在服务器的 url 地址,前端直接使用 a 标签进行下载。关于权限问题,可以使用 cookie 等其他技术方案解决。
isRefreshingrefreshTokenQueue

这个会在后面的拦截器中用到,目前不做讨论。

axios实例,这个也没啥好说的
postrequest请求方法,这个也没啥好说的

问题讨论

1. 为什么进行封装?

目前,我们从上面学习得到的结论就是,通过封装,为调用者开发提供了便利,添加拦截器更加方便,自定义一些请求方法也方便,如下载和上传。

2. 为什么通过加一层进行封装?

减少对源码的污染,以及提高可读性和可维护性。

3. 为什么选择class进行封装?

class更加语义化,可读性和可维护性更高一些。

下集预告:预设拦截器

下面是 vben 中封装的 axios 代码

ts 复制代码
class RequestClient {
  public addRequestInterceptor: InterceptorManager["addRequestInterceptor"];

  public addResponseInterceptor: InterceptorManager["addResponseInterceptor"];
  public download: FileDownloader["download"];

  // 是否正在刷新token
  public isRefreshing = false;
  // 刷新token队列
  public refreshTokenQueue: ((token: string) => void)[] = [];
  public upload: FileUploader["upload"];
  private readonly instance: AxiosInstance;

  /**
   * 构造函数,用于创建Axios实例
   * @param options - Axios请求配置,可选
   */
  constructor(options: RequestClientOptions = {}) {
    // 合并默认配置和传入的配置
    const defaultConfig: RequestClientOptions = {
      headers: {
        "Content-Type": "application/json;charset=utf-8",
      },
      responseReturn: "raw",
      // 默认超时时间
      timeout: 10_000,
    };
    const { ...axiosConfig } = options;
    const requestConfig = merge(axiosConfig, defaultConfig);
    requestConfig.paramsSerializer = getParamsSerializer(requestConfig.paramsSerializer);
    this.instance = axios.create(requestConfig);

    bindMethods(this);

    // 实例化拦截器管理器
    const interceptorManager = new InterceptorManager(this.instance);
    this.addRequestInterceptor = interceptorManager.addRequestInterceptor.bind(interceptorManager);
    this.addResponseInterceptor = interceptorManager.addResponseInterceptor.bind(interceptorManager);

    // 实例化文件上传器
    const fileUploader = new FileUploader(this);
    this.upload = fileUploader.upload.bind(fileUploader);
    // 实例化文件下载器
    const fileDownloader = new FileDownloader(this);
    this.download = fileDownloader.download.bind(fileDownloader);
  }

  /**
   * GET请求方法
   */
  public get<T = any>(url: string, config?: RequestClientConfig): Promise<T> {
    return this.request<T>(url, { ...config, method: "GET" });
  }

  /**
   * POST请求方法
   */
  public post<T = any>(url: string, data?: any, config?: RequestClientConfig): Promise<T> {
    return this.request<T>(url, { ...config, data, method: "POST" });
  }

  /**
   * 通用的请求方法
   */
  public async request<T>(url: string, config: RequestClientConfig): Promise<T> {
    try {
      const response: AxiosResponse<T> = await this.instance({
        url,
        ...config,
        ...(config.paramsSerializer ? { paramsSerializer: getParamsSerializer(config.paramsSerializer) } : {}),
      });
      return response as T;
    } catch (error: any) {
      throw error.response ? error.response.data : error;
    }
  }
}
相关推荐
拾光拾趣录1 分钟前
常见 HTTP 请求头:从“为什么接口返回乱码”说起
前端·http
阿华的代码王国2 分钟前
【Android】卡片式布局 && 滚动容器ScrollView
android·xml·java·前端·后端·卡片布局·滚动容器
2025年一定要上岸8 分钟前
【pytest高阶】源码的走读方法及插件hook
运维·前端·python·pytest
姑苏洛言12 分钟前
答题抽奖活动小程序技术复盘
前端
幻风_huanfeng27 分钟前
学习人工智能所需知识体系及路径详解
人工智能·学习
砖头拍死你1 小时前
51单片机如何使用printf打印unsigned long的那些事
java·前端·51单片机
试着1 小时前
零基础学习性能测试第六章:性能难点-Jmeter文件上传场景压测
学习·jmeter·零基础·性能测试
用户1512905452201 小时前
css —pointer-events属性_css pointer-events
前端
帅夫帅夫1 小时前
Axios 入门指南:从基础用法到实战技巧
前端
云边散步1 小时前
《校园生活平台从 0 到 1 的搭建》第四篇:微信授权登录前端
前端·javascript·后端