解析NaiveUiAdmin的vue开源项目(二)

 书接上回 

解析NaiveUiAdmin的vue开源项目(一)-CSDN博客

本章我们来看until下的包

 src/utils/http/axios/Axios.ts

import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';

import axios from 'axios';
import { AxiosCanceler } from './axiosCancel';
import { isFunction } from '@/utils/is';
import { cloneDeep } from 'lodash-es';

import type { RequestOptions, CreateAxiosOptions, Result, UploadFileParams } from './types';
import { ContentTypeEnum } from '@/enums/httpEnum';

export * from './axiosTransform';

/**
 * @description:  axios模块
 */
export class VAxios {
  private axiosInstance: AxiosInstance;
  private options: CreateAxiosOptions;

  constructor(options: CreateAxiosOptions) {
    this.options = options;
    this.axiosInstance = axios.create(options);
    this.setupInterceptors();
  }

  getAxios(): AxiosInstance {
    return this.axiosInstance;
  }

  /**
   * @description: 重新配置axios
   */
  configAxios(config: CreateAxiosOptions) {
    if (!this.axiosInstance) {
      return;
    }
    this.createAxios(config);
  }

  /**
   * @description: 设置通用header
   */
  setHeader(headers: any): void {
    if (!this.axiosInstance) {
      return;
    }
    Object.assign(this.axiosInstance.defaults.headers, headers);
  }

  /**
   * @description:   请求方法
   */
  request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
    let conf: AxiosRequestConfig = cloneDeep(config);
    const transform = this.getTransform();

    const { requestOptions } = this.options;

    const opt: RequestOptions = Object.assign({}, requestOptions, options);

    const { beforeRequestHook, requestCatch, transformRequestData } = transform || {};
    if (beforeRequestHook && isFunction(beforeRequestHook)) {
      conf = beforeRequestHook(conf, opt);
    }

    //这里重新 赋值成最新的配置
    // @ts-ignore
    conf.requestOptions = opt;

    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request<any, AxiosResponse<Result>>(conf)
        .then((res: AxiosResponse<Result>) => {
          // 请求是否被取消
          const isCancel = axios.isCancel(res);
          if (transformRequestData && isFunction(transformRequestData) && !isCancel) {
            try {
              const ret = transformRequestData(res, opt);
              resolve(ret);
            } catch (err) {
              reject(err || new Error('request error!'));
            }
            return;
          }
          resolve(res as unknown as Promise<T>);
        })
        .catch((e: Error) => {
          if (requestCatch && isFunction(requestCatch)) {
            reject(requestCatch(e));
            return;
          }
          reject(e);
        });
    });
  }

  /**
   * @description:  创建axios实例
   */
  private createAxios(config: CreateAxiosOptions): void {
    this.axiosInstance = axios.create(config);
  }

  private getTransform() {
    const { transform } = this.options;
    return transform;
  }

  /**
   * @description:  文件上传
   */
  uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
    const formData = new window.FormData();
    const customFilename = params.name || 'file';

    if (params.filename) {
      formData.append(customFilename, params.file, params.filename);
    } else {
      formData.append(customFilename, params.file);
    }

    if (params.data) {
      Object.keys(params.data).forEach((key) => {
        const value = params.data![key];
        if (Array.isArray(value)) {
          value.forEach((item) => {
            formData.append(`${key}[]`, item);
          });
          return;
        }

        formData.append(key, params.data![key]);
      });
    }

    return this.axiosInstance.request<T>({
      method: 'POST',
      data: formData,
      headers: {
        'Content-type': ContentTypeEnum.FORM_DATA,
        ignoreCancelToken: true,
      },
      ...config,
    });
  }

  /**
   * @description: 拦截器配置
   */
  private setupInterceptors() {
    const transform = this.getTransform();
    if (!transform) {
      return;
    }
    const {
      requestInterceptors,
      requestInterceptorsCatch,
      responseInterceptors,
      responseInterceptorsCatch,
    } = transform;

    const axiosCanceler = new AxiosCanceler();

    // 请求拦截器配置处理
    this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
      const {
        headers: { ignoreCancelToken },
      } = config;
      const ignoreCancel =
        ignoreCancelToken !== undefined
          ? ignoreCancelToken
          : this.options.requestOptions?.ignoreCancelToken;

      !ignoreCancel && axiosCanceler.addPending(config);
      if (requestInterceptors && isFunction(requestInterceptors)) {
        config = requestInterceptors(config, this.options);
      }
      return config;
    }, undefined);

    // 请求拦截器错误捕获
    requestInterceptorsCatch &&
      isFunction(requestInterceptorsCatch) &&
      this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);

    // 响应结果拦截器处理
    this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
      res && axiosCanceler.removePending(res.config);
      if (responseInterceptors && isFunction(responseInterceptors)) {
        res = responseInterceptors(res);
      }
      return res;
    }, undefined);

    // 响应结果拦截器错误捕获
    responseInterceptorsCatch &&
      isFunction(responseInterceptorsCatch) &&
      this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
  }
}

以下是对这段代码按照代码块进行的解析:

一、导入模块和工具函数

import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
import axios from 'axios';
import { AxiosCanceler } from './axiosCancel';
import { isFunction } from '@/utils/is';
import { cloneDeep } from 'lodash-es';

import type { RequestOptions, CreateAxiosOptions, Result, UploadFileParams } from './types';
import { ContentTypeEnum } from '@/enums/httpEnum';

export * from './axiosTransform';
  • axios库导入AxiosRequestConfig(请求配置类型)、AxiosInstance(Axios 实例类型)、AxiosResponse(响应类型)。
  • 导入自定义的AxiosCanceler类,用于管理取消请求。
  • 从特定路径导入判断是否为函数的工具函数isFunction和用于深拷贝的cloneDeep函数。
  • 导入自定义的类型定义和枚举类型,用于规范请求和响应的参数以及内容类型。

这里有个插曲:

在 Vue 3 项目中,“./” 和 “@/” 的区别如下:

“./”

  1. 相对路径:
    • 在 Vue 3 项目中,“./” 表示当前文件所在的目录。如果在一个组件文件中使用 “./anotherComponent.vue”,它会尝试在与当前组件文件相同的目录下查找名为 “anotherComponent.vue” 的文件。
    • 相对路径的使用会随着文件位置的变化而变化。如果当前文件的位置发生改变,相对路径所指向的资源位置也需要相应调整,否则可能会导致资源无法正确加载。

“@/”

  1. 别名路径:
    • 在 Vue 3 项目中,通常在构建工具(如 Vite 或 Vue CLI)的配置中,“@/” 被定义为项目根目录的别名。
    • 例如,“@/components/MyComponent.vue” 总是指向项目根目录下的 “components” 文件夹中的 “MyComponent.vue” 文件,无论当前文件在项目的哪个位置。
    • 这种别名的使用可以提高代码的可维护性和可读性。开发人员不需要关心文件的具体深度,只需要使用统一的别名来引用项目中的资源。而且,当项目结构发生变化时,只要别名的定义不变,代码中的资源引用通常不需要进行大量的修改。

综上所述,在 Vue 3 项目中,“./” 是相对路径的表示,容易受到文件位置变化的影响;而 “@/” 是通过构建工具配置的别名,指向项目根目录,提供了更稳定和方便的资源引用方式。

为什么这个文件是src/utils/http/axios/Axios.ts,结果可以存在export * from './axiosTransform';这行代码,而axiosTransform.ts在src/utils/http/axios/axiosTransform.ts目录下?

在这个文件结构中存在export * from './axiosTransform';这样的语句是为了进行模块的整合和复用。

模块拆分的好处

  1. 代码组织

    • 将不同的功能拆分成独立的文件有助于更好地组织代码。在这个例子中,Axios.ts主要负责创建和配置 Axios 实例以及处理各种请求相关的操作,而axiosTransform.ts可能包含一些特定的对 Axios 请求和响应进行转换的逻辑。这样可以使每个文件的职责更加清晰,便于维护和扩展。
  2. 可维护性

    • 当需要对 Axios 的请求或响应进行特定的转换操作时,可以直接在axiosTransform.ts文件中进行修改,而不会影响到Axios.ts中其他与请求和响应处理无关的部分。这种分离使得代码的维护更加容易,特别是在大型项目中,不同的开发人员可以专注于不同的模块进行开发和维护。

模块导出和导入的作用

  1. 复用代码

    • 通过在Axios.ts中导出特定的功能模块,并在其他文件中导入使用,可以实现代码的复用。例如,其他模块可能需要使用经过axiosTransform.ts处理后的请求或响应数据,通过这种方式可以方便地获取这些数据,而无需重复编写相同的转换逻辑。
  2. 灵活扩展

    • 可以根据项目的需求,随时在axiosTransform.ts中添加新的转换逻辑,并在Axios.ts中使用这些新的功能,而不会对整个项目的其他部分造成较大的影响。这种灵活性使得项目能够更好地适应不断变化的需求。

我们接着分析源代码。

二、定义类

export class VAxios {
  private axiosInstance: AxiosInstance;
  private options: CreateAxiosOptions;

这个类的设计目的是封装 Axios 的功能,通过提供自定义的配置和方法,方便在项目中进行 HTTP 请求的管理和处理。这些成员变量在类的构造函数中被初始化,并在其他方法中被使用,以实现各种与 HTTP 请求相关的功能。

  • 定义了一个名为VAxios的类,用于封装 Axios 的功能。
  • 以下是对这段代码中类定义部分的分析:

    类的成员变量

  • private axiosInstance: AxiosInstance
    • 这是一个私有成员变量,类型为AxiosInstance,代表 Axios 的实例。这个实例将用于发送 HTTP 请求。其目的是提供一个可复用的 Axios 实例,方便在整个类中进行请求操作,避免重复创建实例带来的性能开销。
  • private options: CreateAxiosOptions
    • 同样是私有成员变量,类型为CreateAxiosOptions,用于存储创建 Axios 实例时所需的配置选项。目的是保存配置信息,以便在类的其他方法中可以根据这些配置进行请求的定制化处理。

三、构造函数

constructor(options: CreateAxiosOptions) {
  this.options = options;
  this.axiosInstance = axios.create(options);
  this.setupInterceptors();
}

接收一个CreateAxiosOptions类型的参数options,用于配置 Axios。

  • 将传入的配置保存到this.options
  • 使用axios.create(options)创建一个 Axios 实例,并保存到this.axiosInstance
  • 调用setupInterceptors方法设置拦截器。

初始化成员变量:

  1. this.options = options;
    • 将传入的配置对象options赋值给类的私有成员变量options。这样可以在类的其他方法中访问和使用这些配置选项。目的是保存外部传入的配置,以便后续根据这些配置进行请求的定制化。
  2. this.axiosInstance = axios.create(options);
    • 使用axios.create(options)创建一个 Axios 实例,并将其赋值给类的私有成员变量axiosInstance。这个实例将用于发送实际的 HTTP 请求。通过传入配置对象,可以对 Axios 进行定制化配置,例如设置请求的基础 URL、超时时间、请求头等等。目的是创建一个可用于发送请求的 Axios 实例,并根据配置进行初始化,以满足项目的特定需求。

设置拦截器

  1. this.setupInterceptors();
    • 调用类内部的setupInterceptors方法来设置请求和响应的拦截器。拦截器可以在请求发送前和响应返回后进行一些预处理和后处理操作。例如,可以在请求拦截器中添加请求头、处理请求参数,在响应拦截器中处理错误、转换响应数据等。通过设置拦截器,可以实现对 HTTP 请求的统一管理和增强功能。目的是为了对请求和响应进行统一的处理,提高代码的可维护性和可扩展性。

四、获取 Axios 实例方法

getAxios(): AxiosInstance {
  return this.axiosInstance;
}

返回创建的 Axios 实例,方便外部获取实例进行其他操作。

五、重新配置 Axios 方法

configAxios(config: CreateAxiosOptions) {
  if (!this.axiosInstance) {
    return;
  }
  this.createAxios(config);
}
  • 接收新的配置参数config,如果当前没有 Axios 实例则直接返回。
  • 否则调用createAxios方法重新创建 Axios 实例。

六、设置通用 header 方法

setHeader(headers: any): void {
  if (!this.axiosInstance) {
    return;
  }
  Object.assign(this.axiosInstance.defaults.headers, headers);
}

  • 接收一个headers对象,用于设置通用请求头。
  • 如果没有 Axios 实例则返回,否则使用Object.assign将传入的请求头合并到 Axios 实例的默认请求头中。

七、请求方法

request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
  let conf: AxiosRequestConfig = cloneDeep(config);
  const transform = this.getTransform();

  const { requestOptions } = this.options;
  const opt: RequestOptions = Object.assign({}, requestOptions, options);

  const { beforeRequestHook, requestCatch, transformRequestData } = transform || {};
  if (beforeRequestHook && isFunction(beforeRequestHook)) {
    conf = beforeRequestHook(conf, opt);
  }

  conf.requestOptions = opt;

  return new Promise((resolve, reject) => {
    this.axiosInstance
     .request<any, AxiosResponse<Result>>(conf)
     .then((res: AxiosResponse<Result>) => {
        const isCancel = axios.isCancel(res);
        if (transformRequestData && isFunction(transformRequestData) &&!isCancel) {
          try {
            const ret = transformRequestData(res, opt);
            resolve(ret);
          } catch (err) {
            reject(err || new Error('request error!'));
          }
          return;
        }
        resolve(res as unknown as Promise<T>);
      })
     .catch((e: Error) => {
        if (requestCatch && isFunction(requestCatch)) {
          reject(requestCatch(e));
          return;
        }
        reject(e);
      });
  });
}

  • 接收请求配置config和可选的请求选项options
  • 首先对传入的请求配置进行深拷贝,保存到conf变量中。
  • 获取当前实例的转换配置transform
  • 合并默认请求选项和传入的请求选项,得到最终的请求选项opt
  • 如果存在请求前的钩子函数beforeRequestHook并且是函数,则调用该函数对请求配置进行预处理。
  • 设置处理后的请求配置的requestOptions为最终的请求选项。
  • 使用 Axios 实例发送请求,在请求成功的回调中:
    • 判断请求是否被取消,如果没有被取消并且存在响应数据转换函数transformRequestData并且是函数,则调用该函数对响应数据进行转换,然后解析结果。
    • 如果没有转换函数或者请求被取消,则直接将响应结果作为 Promise 的解析值返回。
  • 在请求失败的回调中:
    • 如果存在请求错误处理函数requestCatch并且是函数,则调用该函数处理错误,将处理后的错误作为 Promise 的拒绝值返回。
    • 如果没有错误处理函数,则直接将错误作为 Promise 的拒绝值返回。

八、文件上传方法

uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
  const formData = new window.FormData();
  const customFilename = params.name || 'file';

  if (params.filename) {
    formData.append(customFilename, params.file, params.filename);
  } else {
    formData.append(customFilename, params.file);
  }

  if (params.data) {
    Object.keys(params.data).forEach((key) => {
      const value = params.data![key];
      if (Array.isArray(value)) {
        value.forEach((item) => {
          formData.append(`${key}[]`, item);
        });
        return;
      }

      formData.append(key, params.data![key]);
    });
  }

  return this.axiosInstance.request<T>({
    method: 'POST',
    data: formData,
    headers: {
      'Content-type': ContentTypeEnum.FORM_DATA,
      ignoreCancelToken: true,
    },
   ...config,
  });
}

  • 接收请求配置config和文件上传参数params
  • 创建一个FormData对象用于构造文件上传的请求体。
  • 根据参数情况将文件和其他数据添加到FormData中。
  • 使用 Axios 实例发送 POST 请求,设置请求头的内容类型为ContentTypeEnum.FORM_DATA,并将请求配置和FormData作为参数传递。

九、创建 Axios 实例方法

private createAxios(config: CreateAxiosOptions): void {
  this.axiosInstance = axios.create(config);
}

  • 接收新的配置参数config,重新创建 Axios 实例并保存到this.axiosInstance

十、获取转换函数方法

private getTransform() {
  const { transform } = this.options;
  return transform;
}

  • 获取当前实例的配置中的转换函数配置。

十一、拦截器配置方法

private setupInterceptors() {
  const transform = this.getTransform();
  if (!transform) {
    return;
  }
  const {
    requestInterceptors,
    requestInterceptorsCatch,
    responseInterceptors,
    responseInterceptorsCatch,
  } = transform;

  const axiosCanceler = new AxiosCanceler();

  // 请求拦截器配置处理
  this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
    const {
      headers: { ignoreCancelToken },
    } = config;
    const ignoreCancel =
      ignoreCancelToken!== undefined
       ? ignoreCancelToken
        : this.options.requestOptions?.ignoreCancelToken;

   !ignoreCancel && axiosCanceler.addPending(config);
    if (requestInterceptors && isFunction(requestInterceptors)) {
      config = requestInterceptors(config, this.options);
    }
    return config;
  }, undefined);

  // 请求拦截器错误捕获
  requestInterceptorsCatch &&
    isFunction(requestInterceptorsCatch) &&
    this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);

  // 响应结果拦截器处理
  this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
    res && axiosCanceler.removePending(res.config);
    if (responseInterceptors && isFunction(responseInterceptors)) {
      res = responseInterceptors(res);
    }
    return res;
  }, undefined);

  // 响应结果拦截器错误捕获
  responseInterceptorsCatch &&
    isFunction(responseInterceptorsCatch) &&
    this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
}

  • 首先获取转换函数配置,如果不存在则直接返回。
  • 创建一个AxiosCanceler实例用于管理取消请求。
  • 设置请求拦截器:
    • 如果配置了忽略取消请求标记,则不进行处理;否则将请求添加到待取消请求列表中。
    • 如果存在请求拦截器函数requestInterceptors并且是函数,则调用该函数对请求进行预处理。
    • 如果存在请求拦截器错误捕获函数requestInterceptorsCatch并且是函数,则在请求发生错误时调用该函数进行处理。
  • 设置响应拦截器:
    • 在响应返回后,从待取消请求列表中移除该请求。
    • 如果存在响应拦截器函数responseInterceptors并且是函数,则调用该函数对响应进行后处理。
    • 如果存在响应拦截器错误捕获函数responseInterceptorsCatch并且是函数,则在响应发生错误时调用该函数进行处理

以下是对上述代码主要功能的汇总

一、功能概述

这段代码是一个在 Vue 3 项目中对 Axios 进行封装的模块,主要目的是提供更方便、可配置且功能强大的 HTTP 请求工具,同时支持请求取消、请求和响应的拦截处理以及文件上传等功能。

二、主要功能点

  1. Axios 实例创建与配置

    • 通过接收一个配置对象CreateAxiosOptions,使用axios.create(options)创建一个 Axios 实例,并保存在VAxios类中。
    • 可以通过configAxios方法重新配置 Axios 实例。
  2. 通用请求头设置

    • setHeader方法可以设置通用的请求头,方便在整个项目中统一设置一些常用的请求头信息。
  3. 请求发送

    • request方法用于发送 HTTP 请求,可以接收请求配置和可选的请求选项。
    • 在发送请求前,可以通过请求前的钩子函数beforeRequestHook对请求配置进行预处理。
    • 支持对请求错误的处理,通过requestCatch函数可以捕获和处理请求错误。
    • 如果配置了响应数据转换函数transformRequestData,可以对响应数据进行转换后再返回给调用者。
  4. 文件上传

    • uploadFile方法专门用于文件上传,使用FormData构造请求体,可以同时上传文件和其他数据。
    • 设置了特定的内容类型ContentTypeEnum.FORM_DATA,并支持将请求配置与文件上传参数结合起来发送请求。
  5. 拦截器配置

    • setupInterceptors方法设置了请求和响应的拦截器。
    • 请求拦截器:
      • 在请求发送前,检查是否需要忽略取消请求标记,如果不需要则将请求添加到待取消请求列表中。
      • 可以通过请求拦截器函数requestInterceptors对请求进行预处理。
      • 如果配置了请求拦截器错误捕获函数requestInterceptorsCatch,可以在请求发生错误时进行处理。
    • 响应拦截器:
      • 在响应返回后,从待取消请求列表中移除该请求。
      • 可以通过响应拦截器函数responseInterceptors对响应进行后处理。
      • 如果配置了响应拦截器错误捕获函数responseInterceptorsCatch,可以在响应发生错误时进行处理。
  6. 模块复用与扩展

    • 通过export * from './axiosTransform';可以复用axiosTransform.ts文件中的功能模块,可能包含特定的对 Axios 请求和响应进行转换的逻辑,方便进行灵活的扩展和维护。

总之,这段代码为 Vue 3 项目提供了一个强大的 HTTP 请求工具,使得项目中的网络请求处理更加规范、可配置和易于维护。

这段工具代码中可以供外界调用的方法有:

方法

  1. request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T>:用于发送 HTTP 请求,可以传入请求配置和可选的请求选项,并返回一个 Promise,最终解析为请求的响应数据或者抛出错误。
  2. uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams):用于文件上传,接收请求配置和文件上传参数,返回一个 Promise,最终解析为文件上传的响应数据或者抛出错误。
  3. getAxios(): AxiosInstance:返回创建的 Axios 实例,外部可以通过这个方法获取 Axios 实例进行一些高级操作或者直接使用 Axios 的其他方法。
  4. configAxios(config: CreateAxiosOptions):用于重新配置 Axios 实例,可以传入新的配置对象来更新 Axios 的配置。
  5. setHeader(headers: any):用于设置通用的请求头,可以传入一个包含请求头信息的对象来更新 Axios 实例的默认请求头。

以上是通过方法的访问修饰符和命名约定判断出来

  1. getAxios()方法:

    • 命名明确表明其目的是获取 Axios 实例供外部使用。通常以 “get” 开头的方法是为了提供对内部状态或资源的访问。
    • 没有任何访问限制修饰符,意味着在外部可以直接调用这个方法来获取AxiosInstance
  2. request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T>方法:

    • 作为一个执行请求操作的方法,其目的是供外部发起 HTTP 请求。通常这样的方法会被设计为可被外部调用以满足项目中对数据获取的需求。
    • 没有访问限制修饰符,可在外部直接调用以发送请求并获得响应数据或处理错误。
  3. uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams)方法:

    • 从命名可以看出是用于文件上传的方法,在实际项目中,如果有文件上传的需求,外部必然需要调用这个方法来执行文件上传操作。
    • 没有访问限制修饰符,可被外部调用进行文件上传。
  4. configAxios(config: CreateAxiosOptions)方法:

    • 用于重新配置 Axios 实例,在项目运行过程中,如果需要动态地更改 Axios 的配置,外部可以调用这个方法传入新的配置选项来实现重新配置。
    • 没有访问限制修饰符,可被外部调用以更新配置。
  5. setHeader(headers: any)方法:

    • 用于设置通用请求头,在外部需要修改请求头信息时,可以调用这个方法来实现。
    • 没有访问限制修饰符,可被外部调用以设置请求头。

这里有段插曲:

this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
      const {
        headers: { ignoreCancelToken },
      } = config;
      const ignoreCancel =
        ignoreCancelToken !== undefined
          ? ignoreCancelToken
          : this.options.requestOptions?.ignoreCancelToken;

      !ignoreCancel && axiosCanceler.addPending(config);
      if (requestInterceptors && isFunction(requestInterceptors)) {
        config = requestInterceptors(config, this.options);
      }
      return config;
    },

以上代码报错了,让我们来分析一下:

这段代码是设置 Axios 的请求拦截器。其主要目的是在发送请求之前对请求配置进行一些处理和决策。

具体步骤解释

  1. 提取请求配置中的ignoreCancelToken

    • 从传入的请求配置config中获取请求头信息,然后再从请求头中提取出ignoreCancelToken。如果这个标记存在,它将决定是否忽略请求的取消操作。
    • const { headers: { ignoreCancelToken } } = config;通过解构赋值从请求配置中获取请求头,再进一步获取ignoreCancelToken
  2. 确定是否忽略取消请求:

    • 如果ignoreCancelToken有明确的值,那么就使用这个值来决定是否忽略取消请求。如果ignoreCancelToken未定义,则检查当前实例的配置选项中的requestOptions里是否有ignoreCancelToken,如果有则使用这个值,否则默认不忽略取消请求。
    • const ignoreCancel = ignoreCancelToken!== undefined? ignoreCancelToken : this.options.requestOptions?.ignoreCancelToken;通过条件判断确定最终的ignoreCancel值。
  3. 处理取消请求:

    • 如果不忽略取消请求(即!ignoreCancel为真),则将当前请求配置添加到一个取消请求管理器(axiosCanceler.addPending(config))中。这样可以在需要的时候取消这个请求。
    • !ignoreCancel && axiosCanceler.addPending(config);通过逻辑与操作符确保只有在不忽略取消请求时才执行添加请求到待取消列表的操作。
  4. 调用自定义的请求拦截器函数:

    • 如果存在自定义的请求拦截器函数requestInterceptors并且它是一个函数,那么就调用这个函数,并将请求配置和当前实例的配置选项作为参数传入。这个函数可以对请求配置进行进一步的处理和修改。
    • if (requestInterceptors && isFunction(requestInterceptors)) { config = requestInterceptors(config, this.options); }通过条件判断和函数类型检查确保安全地调用自定义拦截器函数,并更新请求配置。
  5. 返回处理后的请求配置:

    • 最后,将处理后的请求配置返回,以便 Axios 继续使用这个配置发送请求。
    • return config;确保修改后的请求配置被传递给后续的请求发送流程。

以下是对这段代码进行分块分析:

this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
    // 1. 提取请求配置中的 ignoreCancelToken
    const {
        headers: { ignoreCancelToken },
    } = config;
    // 2. 确定是否忽略取消请求
    const ignoreCancel =
        ignoreCancelToken!== undefined
           ? ignoreCancelToken
            : this.options.requestOptions?.ignoreCancelToken;
    // 3. 处理取消请求
   !ignoreCancel && axiosCanceler.addPending(config);
    // 4. 调用自定义的请求拦截器函数
    if (requestInterceptors && isFunction(requestInterceptors)) {
        config = requestInterceptors(config, this.options);
    }
    // 5. 返回处理后的请求配置
    return config;
}, undefined);

提取请求配置中的ignoreCancelToken

const {
    headers: { ignoreCancelToken },
} = config;

从传入的请求配置config中,首先获取请求头信息,然后再从请求头中尝试提取出名为ignoreCancelToken的属性。这个属性可能用于决定是否忽略请求的取消操作

确定是否忽略取消请求

const ignoreCancel =
    ignoreCancelToken!== undefined
       ? ignoreCancelToken
        : this.options.requestOptions?.ignoreCancelToken;

如果在请求配置的头信息中ignoreCancelToken有明确的值,那么就使用这个值来决定是否忽略取消请求。如果ignoreCancelToken未定义,则检查当前实例的配置选项中的requestOptions里是否有ignoreCancelToken,如果有则使用这个值,否则默认不忽略取消请求。

处理取消请求

!ignoreCancel && axiosCanceler.addPending(config);

如果不忽略取消请求(即!ignoreCancel为真),则将当前请求配置添加到一个取消请求管理器(axiosCanceler.addPending(config))中。这样可以在需要的时候取消这个请求。

调用自定义的请求拦截器函数

if (requestInterceptors && isFunction(requestInterceptors)) {
    config = requestInterceptors(config, this.options);
}

如果存在自定义的请求拦截器函数requestInterceptors并且它是一个函数,那么就调用这个函数,并将请求配置和当前实例的配置选项作为参数传入。这个函数可以对请求配置进行进一步的处理和修改。

返回处理后的请求配置

return config;

最后,将处理后的请求配置返回,以便 Axios 继续使用这个配置发送请求。

一、第一个报错信息

Vue: Argument of type '(config: AxiosRequestConfig) => AxiosRequestConfig<any>' is not assignable to parameter of type '(value: InternalAxiosRequestConfig<any>) => InternalAxiosRequestConfig<any> | Promise<InternalAxiosRequestConfig<any>>'.Type 'AxiosRequestConfig<any>' is not assignable to type 'InternalAxiosRequestConfig<any> | Promise<InternalAxiosRequestConfig<any>>'.

  1. 解释:

    • 这个报错表明在 Vue 项目中,你正在尝试将一个函数类型为(config: AxiosRequestConfig) => AxiosRequestConfig<any>的参数传递给一个期望函数类型为(value: InternalAxiosRequestConfig<any>) => InternalAxiosRequestConfig<any> | Promise<InternalAxiosRequestConfig<any>>的地方。
    • AxiosRequestConfig<any>InternalAxiosRequestConfig<any>可能是不同的类型定义,或者存在一些不兼容的属性或结构差异。
    • 这意味着你的函数返回值类型不满足接收它的函数所期望的类型要求。
  2. 可能的解决方案:

    • 检查你的代码中涉及到这个函数参数传递的地方,确保你传递的函数的输入参数和返回值类型与期望的类型匹配。
    • 如果可能,调整你的函数的输入参数和返回值类型,使其符合接收函数的要求。
    • 确认AxiosRequestConfig<any>InternalAxiosRequestConfig<any>之间的差异,并进行适当的类型转换或调整代码逻辑以解决类型不匹配的问题。

报错分析

  1. 传入函数类型与期望类型不匹配

    • 在 Vue 项目中,通常在设置 Axios 请求拦截器时出现了这个问题。Axios 的拦截器机制期望特定类型的函数作为参数。
    • 传入的函数接收AxiosRequestConfig类型参数并返回AxiosRequestConfig<any>。这意味着传入的函数被设计为处理AxiosRequestConfig类型的请求配置,并返回一个经过处理后的相同类型的配置对象。
    • 然而,Axios 内部期望的函数接收InternalAxiosRequestConfig<any>类型参数并返回InternalAxiosRequestConfig<any>Promise<InternalAxiosRequestConfig<any>>。这表明 Axios 内部对于请求配置的类型定义与外部传入的函数所处理的类型不一致。
    • AxiosRequestConfig<any>InternalAxiosRequestConfig<any>之间可能存在一些不兼容的属性或结构差异。这可能是因为 Axios 内部对请求配置进行了更严格的类型定义或者添加了一些特定的属性和方法,而外部传入的函数并没有考虑到这些差异。
  2. 可能的不兼容原因

    • 属性差异:两个类型可能在某些属性的类型定义上不同。例如,某个属性在AxiosRequestConfig中是一个简单类型,但在InternalAxiosRequestConfig中是一个复杂的对象类型。
    • 方法差异:InternalAxiosRequestConfig可能包含一些特定的方法,而AxiosRequestConfig中没有这些方法。这可能导致在调用某些特定的 Axios 内部功能时出现问题。
    • 嵌套结构差异:如果请求配置对象包含嵌套的对象或数组,两个类型在嵌套结构的定义上可能不同,导致在处理复杂的请求配置时出现不兼容的情况。

解决方案分析

  1. 检查设置请求拦截器的地方

    • 首先,需要找到在代码中设置 Axios 请求拦截器的位置。通常是在使用axiosInstance.interceptors.request.use()或类似方法的地方。
    • 仔细检查传入的函数是否正确地处理了请求配置对象。确保函数的逻辑与 Axios 期望的请求配置类型相匹配。
  2. 进行类型转换

    • 如果确定AxiosRequestConfigInternalAxiosRequestConfig之间存在一些简单的类型差异,可以考虑进行类型转换。例如,可以使用类型断言或类型转换函数将AxiosRequestConfig类型的对象转换为InternalAxiosRequestConfig类型。
    • 但是,在进行类型转换时要小心,确保转换是安全的,不会导致数据丢失或错误的行为。最好在进行类型转换之前,对两个类型的结构进行详细的比较,以确保转换的正确性。
  3. 调整函数的输入参数和返回值类型

    • 另一种解决方案是调整传入函数的输入参数和返回值类型,使其与InternalAxiosRequestConfig<any>兼容。
    • 这可能需要修改函数的定义,将输入参数的类型从AxiosRequestConfig改为InternalAxiosRequestConfig<any>,并相应地调整函数内部的逻辑,以处理新的类型。
    • 对于返回值类型,也需要确保它符合 Axios 期望的类型,可以是InternalAxiosRequestConfig<any>Promise<InternalAxiosRequestConfig<any>>。如果函数的逻辑不适合返回Promise,可以考虑将其修改为同步操作,直接返回InternalAxiosRequestConfig<any>类型的对象。

总之,这个报错提示了在 Vue 项目中使用 Axios 时可能出现的类型不匹配问题。通过仔细检查代码中设置请求拦截器的地方,并采取适当的解决方案,如进行类型转换或调整函数的类型,可以解决这个问题,确保 Axios 的正常运行。

二、第二个报错信息

Vue: Property 'ignoreCancelToken' does not exist on type 'AxiosHeaders | (Partial<RawAxiosHeaders & { Accept: AxiosHeaderValue; "Content-Length": AxiosHeaderValue; "User-Agent": AxiosHeaderValue; "Content-Encoding": AxiosHeaderValue; Authorization: AxiosHeaderValue; } & {...; }> & Partial<...>) | undefined'.

  1. 解释:

    • 这个报错指出在 Vue 项目中,你正在尝试访问一个名为ignoreCancelToken的属性,但在给定的类型AxiosHeaders | (Partial<...> & Partial<...>) | undefined上不存在这个属性。
    • 这意味着你正在尝试从一个可能是AxiosHeaders类型、一个复杂的部分类型组合或者undefined的对象上访问ignoreCancelToken属性,但这个属性在这些类型中都没有被定义
  2. 可能的解决方案:

    • 检查你的代码中访问ignoreCancelToken属性的地方,确保你正在从一个正确的类型对象上访问这个属性。
    • 如果ignoreCancelToken不是标准的 Axios 请求头属性,确认你是否正确地添加了这个自定义属性到请求头中。
    • 考虑使用类型断言或类型守卫来确保你正在操作的对象具有ignoreCancelToken属性,或者在访问这个属性之前进行适当的类型检查。

以下是对这个报错信息及解决方案的详细说明:

对于这个报错Vue: Type 'AxiosRequestConfig<any>' is not assignable to type 'InternalAxiosRequestConfig<any>'.Types of property 'headers' are incompatible.,如果你想通过 Ctrl 点击查看相关源码,可以尝试以下几个地方:

  1. AxiosRequestConfigInternalAxiosRequestConfig的类型定义处:通常在你导入axios的地方附近,或者在你的项目中安装的axios类型定义文件中。点击这些类型名称可以查看它们的具体定义,了解它们在headers属性上的差异。
  2. config.headers:点击这个属性访问表达式,查看在不同类型下这个属性的具体类型和可能导致不兼容的原因。
  3. axiosCanceler.addPending(config):点击这个方法调用,查看这个方法对参数类型的期望以及内部是如何处理传入的参数的。
  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值