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;
    }
  }
}
相关推荐
驭风少年君14 小时前
《搭建属于自己的网站之网页前端学习》基础入门
前端·学习
刘一说15 小时前
深入理解 Spring Boot 嵌入式 Web 容器:从原理到性能调优
前端·spring boot·firefox
你的人类朋友15 小时前
设计模式的原则有哪些?
前端·后端·设计模式
!执行16 小时前
Web3 前端与合约交互
前端·web3·1024程序员节
潘小安16 小时前
跟着 AI 学(二)- Quill 接入速通
前端
十里-16 小时前
在 Vue2 中为 Element-UI 的 el-dialog 添加拖拽功能
前端·vue.js·ui
shada16 小时前
从Google Chrome商店下载CRX文件
前端·chrome
CIb0la16 小时前
能保持精神专注的爱好能给生活带来种种积极的转变
运维·学习·生活
左耳咚16 小时前
项目开发中从补码到精度丢失的陷阱
前端·javascript·面试
D_C_tyu16 小时前
Vue3 + Element Plus 实现前端手动分页
javascript·vue.js·elementui