axios二次封装
大约 5 分钟
axios 二次封装
基础封装
首先进行基础封装,http 目录下创建 request/index.ts 文件
import axios from 'axios';
import type {} from 'axios';
import { AxiosRequestConfig } from 'axios';
import { AxiosInstance } from 'axios';
class Request {
  // axios实例
  instance: AxiosInstance;
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config);
  }
  request(config: AxiosRequestConfig) {
    return this.instance.request(config);
  }
}
export default Request;这里封装成一个类,类可以创建多个实例,使用范围更广。
拦截器封装
拦截器分三中:
- 类拦截器
- 实例拦截器
- 接口拦截器
类拦截器
类拦截器比较简单,在类中对 axios.create() 创建的实例调用 interceptors 下的两个拦截器即可
class Request {
  // axios实例
  instance: AxiosInstance;
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config);
    // 类请求拦截
    this.instance.interceptors.request.use(
      (res: InternalAxiosRequestConfig) => {
        console.log('全局请求拦截器');
        return res;
      },
      (err) => err
    );
    // 类响应拦截
    this.instance.interceptors.response.use(
      (res: AxiosResponse) => {
        console.log('全局响应拦截器');
        return res.data;
      },
      (err) => err
    );
  }
}实例拦截器
实例拦截器是为了为了灵活性,为了处理不同类型的拦截操作
先定义下 interface
import type {
  AxiosRequestConfig,
  InternalAxiosRequestConfig,
  AxiosResponse,
} from 'axios';
export interface RequestInterceptors {
  // 请求拦截
  requestInterceptors?: (
    config: InternalAxiosRequestConfig
  ) => InternalAxiosRequestConfig;
  requestInterceptorsCatch?: (err: any) => any;
  // 响应拦截
  responseInterceptors?: (config: AxiosResponse) => AxiosResponse;
  responseInterceptorsCatch?: (err: any) => any;
}
export interface RequestConfig extends AxiosRequestConfig {
  interceptors?: RequestInterceptors;
}将拦截器扩展到 AxiosRequestConfig 中,并在 Request 中使用拦截器
import axios from 'axios';
import type {
  InternalAxiosRequestConfig,
  AxiosRequestConfig,
  AxiosInstance,
  AxiosResponse,
} from 'axios';
import type { RequestInterceptors, RequestConfig } from './types';
class Request {
  // axios实例
  instance: AxiosInstance;
  // 拦截器对象
  interceptorsObj?: RequestInterceptors;
接口拦截
现在对单一接口进行拦截操作,对 request 方法进行改造
import axios from 'axios';
import type {
  InternalAxiosRequestConfig,
  AxiosInstance,
  AxiosResponse,
} from 'axios';
import type { RequestInterceptors, RequestConfig } from './types';
class Request {
  // axios实例
  instance: AxiosInstance;
  // 拦截器对象
  interceptorsObj?: RequestInterceptors;
  request<T>(config: RequestConfig): Promise<T> {
    return new Promise((resolve, reject) => {
      // 如果我们为单个请求设置拦截器,这里使用单个请求的拦截器
      if (config.interceptors?.requestInterceptors) {
        config = config.interceptors.requestInterceptors(
          config as InternalAxiosRequestConfig
        );
      }
      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 如果我们为单个响应设置拦截器,这里使用单个响应的拦截器
          if (config.interceptors?.responseInterceptors) {
            res = config.interceptors.responseInterceptors<T>(res);
          }
调整下 types
export interface RequestInterceptors {
  // 请求拦截
  requestInterceptors?: (
    config: InternalAxiosRequestConfig
  ) => InternalAxiosRequestConfig;
  requestInterceptorsCatch?: (err: any) => any;
  // 响应拦截
  responseInterceptors?: <T = AxiosResponse>(config: T) => T;
  responseInterceptorsCatch?: (err: any) => any;
}封装请求方法
用封装好的 Request 创建实例,并封装不同类型的请求,这里以 http/index.ts 为例
interface HTTPRequestConfig {
  (url: string, data: any, contentType?: string): any;
}
const AJ = 'application/json';
const AXC = 'application/x-www-form-urlencoded;charset=UTF-8';
const request = new Request({
  timeout: 1000 * 60 * 1,
  interceptors: {
    // 请求拦截器
    requestInterceptors: (config) => {
      console.log('实例请求拦截器');
      return config;
    },
    // 响应拦截器
    responseInterceptors: (result) => {
      console.log('实例响应拦截器');
      return result;
    },
  },
});
export const $post: HTTPRequestConfig = (url, data, contentType) => {
  const ct = contentType || AJ;
  return request.request({
    method: 'post',
    url,
    data,
    headers: { 'Content-Type': ct },
  });
};
export const $get: HTTPRequestConfig = function (url, data, contentType) {
  const ct = contentType || AXC;
  data = data || {};
  return request.request({
    method: 'get',
    url,
    params: data,
    headers: {
      'Content-Type': ct,
    },
  });
};
export const $delete: HTTPRequestConfig = (url, data, contentType) => {
  const ct = contentType || AXC;
  data = data || {};
  return request.request({
    method: 'delete',
    url,
    params: data,
    headers: {
      'Content-Type': ct,
    },
  });
};
export const $put: HTTPRequestConfig = (url, data, contentType) => {
  const ct = contentType || AJ;
  data = data || {};
  return request.request({
    method: 'put',
    url,
    data,
    headers: { 'Content-Type': ct },
  });
};
export const $getBlob: HTTPRequestConfig = (url, data, contentType) => {
  const ct = contentType || AXC;
  data = data || {};
  return request.request({
    method: 'get',
    url,
    params: data,
    responseType: 'blob',
    headers: {
      'Content-Type': ct,
    },
  });
};
export const $postBlob: HTTPRequestConfig = (url, data, contentType) => {
  const ct = contentType || AJ;
  return request.request({
    method: 'post',
    url,
    data,
    responseType: 'blob',
    headers: { 'Content-Type': ct },
  });
};
export const $postArraybuffer: HTTPRequestConfig = (url, data, contentType) => {
  const ct = contentType || AJ;
  return request.request({
    method: 'post',
    url,
    data,
    responseType: 'arraybuffer',
    headers: { 'Content-Type': ct },
  });
};使用接口请求
这里以 vue 为实例项目,在 src 下新增 api 文件夹存放接口请求文件
// api/serve/index.ts
import { $get, $post } from '@/http';
export function httpGetData(params: any) {
  return $get('/api/getData', params);
}
export function httpPostAdd(params: any) {
  return $post('/api/add', params);
}在 vue 中调用接口
<script lang="ts" setup>
import { httpGetData } from '@/api/serve';
const getData = async () => {
  const params = {
    name: '',
  };
  const res = await httpGetData(params);
  if (res.code === 200) {
    console.log(`请求成功:${res.data}`);
  }
};
</script>取消请求
什么是取消请求可参考官方文档
准备工作
需要将所有请求的取消方法保存到一个集合,然后根据具体需要去调用这个集合中的某个取消请求方法。
class Request {
  // axios实例
  instance: AxiosInstance;
  // 拦截器对象
  interceptorsObj?: RequestInterceptors;
  // 存放取消方法的集合
  cancelRequestSourceList?: CancelRequestSource[];
  // 存放所有请求URL的集合
  requestUrlList?: string[];
  constructor(config: RequestConfig) {
    // 记录axios实例
    this.instance = axios.create(config);
    this.interceptorsObj = config.interceptors;
    // 数据初始化
    this.cancelRequestSourceList = [];
    this.requestUrlList = [];
  }
}定义 CancelRequestSource
// types.ts
export interface CancelRequestSource {
  [index: string]: () => void;
}取消请求方法的添加与删除
这里需要在 request 中,在请求之前将 url 和取消请求方法 push 到我们前面定义的两个属性中,然后在请求完毕后(不管是失败还是成功)都将其进行删除
class Request {
  /**
   * @description: 取消所有请求
   * @returns {*}
   */
  public cancelAllRequest() {
    this.cancelRequestSourceList?.forEach((source) => {
      const key = Object.keys(source)[0];
      source[key]();
    });
  }
