TypeScript 封装 Axios 1.7.7

随着Axios版本的不同,类型也在改变,以后怎么写类型?

javascript 复制代码
yarn add axios

1. 封装Axios

将Axios封装成一个类,同时重新封装request方法

  • 重新封装request有几个好处:
    1. 所有的请求将从我们定义的requet请求中发送,这样以后更换Axios工具,只需要将request方法重写就可以。
    2. 便于对请求的请求体和返回数据做拦截
    3. 方便统一配置请求的config
  • 最基础版本
typescript 复制代码
import axios from "axios";
import type { AxiosInstance } from "axios";

class MyAxios {
  instance: AxiosInstance;
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config);
  }
// 重新封装axios的request方法
  async request(config: AxiosRequestConfig) {
    return await this.instance.reques(config)
  }
}
export default MyAxios;
  • 发送请求
typescript 复制代码
import MyAxios from "./request";
import { BASE_URL, TIME_OUT} from "./config"; // 配置文件

const myRequest1 = new MyAxios({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
});

myRequest1
  .request({
    url: "/XXXXX",
  })
  .then((res) => {
    // res为成功后返回的结果
  });

2. 加入全局拦截器

  • 加入全局拦截器后封装的Axios,调用方法与上面保持一致
typescript 复制代码
import axios from "axios";
import type { AxiosInstance } from "axios";

class MyAxios {
  instance: AxiosInstance;
  constructor(config: AxiosRequestConfig) {
  	// 创建axios的实例
    this.instance = axios.create(config);
    // 全局请求拦截器
    this.instance.interceptors.request.use(
      (config) => {
        // "全局请求拦截器:请求成功");
        return config;
      },
      (error) => {
        // ("全局请求拦截器:请求失败");
        return error;
      }
    );
    // 全局响应拦截器
    this.instance.interceptors.response.use(
      (res) => {
       // ("全局响应拦截器:响应成功");
       // res中含有axios中的很多东西,res.data :服务器返回的结果,
        return res.data;
      },
      (error) => {
        // ("全局响应拦截器:响应失败");
        return error;
      }
    );
  }

  async request(config: AxiosRequestConfig) {
    return await this.instance.request(config)
  }
}

export default MyAxios;
  • 发送请求
typescript 复制代码
import MyAxios from "./request";
import { BASE_URL, TIME_OUT} from "./config"; // 配置文件

const myRequest1 = new MyAxios({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
});

myRequest1
  .request({
    url: "/XXXXX",
  })
  .then((res) => {
    // res变成了服务器发送会的数据,全局响应拦截器把Axios带的一些数据去掉了
  });

3. 加入域拦截器

域拦截器是我自己想的名字,也可以说是实例拦截器

在实例中定义拦截方法

  1. 拦截的范围是一个baseURL,所以我称它为域拦截
  2. 因为定义在一个new MyAxios实例中,也可以说是实例拦截器
  • 给拦截器定义类型
typescript 复制代码
import type { AxiosRequestConfig } from "axios";

// 自定义拦截器中的方法的类型
interface interceptorsFunction {
  requestOnFulfilled?: (config: any) => any; // 请求成功
  requestOnRejected?: (error: any) => any; //  请求失败
  responseOnFulfilled?: (res: any) => any; //  响应成功
  responseOnRejected?: (error: any) => any; // 响应失败
}
// 自定义拦截器类型。这里扩展了AxiosRequestConfig类型
interface MyAxiosRequestConfig extends AxiosRequestConfig {
  MyInterceptors?: interceptorsFunction;
}

export { MyAxiosRequestConfig };
  • 在实例中实现拦截器的方法
typescript 复制代码
import MyAxios from "./request";
import { BASE_URL, TIME_OUT } from "./config";
import type { MyAxiosRequestConfig } from "./type";

const myRequest2 = new MyAxios({
  baseURL: BASE_URL,	
  timeout: TIME_OUT,
  // 域拦截器。给axios发送的自定义拦截器需要执行的功能
  MyInterceptors: {
    requestOnFulfilled: (config) => {
      // ("自定义请求拦截器:请求成功");
      return config
    },
    requestOnRejected: (error) => {
      // ("自定义请求拦截器:请求失败");
      return error
    },
    responseOnFulfilled: (res) => {
      // ("自定义响应拦截器:响应成功");
      return res
    },
    responseOnRejected: (error) => {
      // ("自定义响应拦截器:响应失败");
      return error
    }
  }
})
// 发送请求
myRequest.myRequest2
  .request({
    url: "XXXXXX",
  })
  .then((res) => {
    // (res);
  });
  • 封装Axois
typescript 复制代码
import axios from "axios";
import type { AxiosInstance } from "axios";
// 导入自定义拦截器的类型
import type { MyAxiosRequestConfig } from "./type";

class MyAxios {
  instance: AxiosInstance;
  constructor(config: MyAxiosRequestConfig) {
    this.instance = axios.create(config);
    // 全局请求拦截器
    this.instance.interceptors.request.use(
      (config) => {
        // ("全局请求拦截器:请求成功");
        return config;
      },
      (error) => {
        // ("全局请求拦截器:请求失败");
        return error;
      }
    );
    // 全局响应拦截器
    this.instance.interceptors.response.use(
      (res) => {
        // ("全局响应拦截器:响应成功");
        return res.data;
      },
      (error) => {
        // ("全局响应拦截器:响应失败");
        return error;
      }
    );
    // 请求中自定义的拦截器:实例的拦截器
    // 判断config中是否有自定义的拦截器
    if (config.MyInterceptors) {
    	// 把config中传过来的方法,绑定到实例上
      this.instance.interceptors.request.use(
        config.MyInterceptors.requestOnFulfilled,
        config.MyInterceptors.requestOnRejected
      );
      this.instance.interceptors.response.use(
        config.MyInterceptors.responseOnFulfilled,
        config.MyInterceptors.responseOnRejected
      );
    }
  }
  async request(config: AxiosRequestConfig<T>) {
    return await this.instance.request(config)
  }
}

export default MyAxios;

4. 关于类型

typescript 复制代码
  requestOnFulfilled?: (config: any) => any; // 请求成功

定义拦截器类型中requestOnFulfilled类型,看的教学视频中config的类型是AxiosRequestConfig,我设置后死活报错,看了一下axios的类型定义文件中这里是InternalAxiosRequestConfig,这里就涉及axios的版本问题了,所以我觉得还是any安全,万一明天更新版本又换了类型,这个代码就不通用了

5. 一次性拦截器

  • 这又是我自己起的名字,这个拦截器针对具体的一个请求

例如:域 http:// 10.1.2.3:8000下面有很多资源,比如有/api/img、/api/text,一次性拦截器就是针对域下面的资源的,可能/api/img需要拦截器工作,而/api/text不需要拦截器,所以每个资源对拦截器的需求和功能是不一样的

  • 封装Axios,主要是对request请求的改动,其他地方没有改动,省略代码,这个拦截器虽然是起到了拦截器的效果,其实就是引入了一段代码
typescript 复制代码
import axios from "axios";
import type { AxiosInstance } from "axios";
// 导入自定义拦截器的类型
import type { MyAxiosRequestConfig } from "./type";

class MyAxios {
  instance: AxiosInstance;
  constructor(config: MyAxiosRequestConfig) {
    this.instance = axios.create(config);
    // 全局请求拦截器

    // 全局响应拦截器

    // 请求中自定义的拦截器:实例的拦截器
  }

  async request(config: MyAxiosRequestConfig) {
  	// config中是否定义了requestOnFulfilled这个方法
    if (config.MyInterceptors?.requestOnFulfilled) {
    // 如果有这个方法,把config对象处理一下
      config = config.MyInterceptors.requestOnFulfilled(config);
    }
    return await this.instance
      .request(config)	// request方法返回Promise
      .then((res) => {	
      	// Promise是成功状态返回的res,如果需要处理就处理一下子
        if (config.MyInterceptors?.responseOnFulfilled) {
          res = config.MyInterceptors.responseOnFulfilled(res);
        }
        return res;
      })
      .catch((error) => {	// Promise是失败状态或者异常,处理一下子
        if (config.MyInterceptors?.responseOnRejected)
          error = config.MyInterceptors.responseOnRejected(error);
        return error;
      });
  }
}

export default MyAxios;
  • 发送请求
typescript 复制代码
const myRequest1 = new MyAxios({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
});
// 发送请求
myRequest1
  .request({
    url: "XXXXXX",
    // 定义一次性拦截器,只对 url: "XXXXX" 起效
    MyInterceptors: {
      requestOnFulfilled: (config) => {
        // ("一次性拦截器,请求成功拦截");
        return config;
      },
      responseOnFulfilled: (res) => {
        // ("一次性拦截器,响应成功拦截");
        return res;
      },
      responseOnRejected: (res) => {
        // ("一次性拦截器,响应失败拦截");
        return res;
      },
    },
  })
  .then((res) => {
    // (res);
  });
  • 拦截器的类型定义与域拦截器中的一样就不重复了

6. 总结

  • 全局拦截器:只要使用Axios发送请求就会执行
  • 域拦截器:对实例中的一个baseURLhttp://10.1.2.3:8080)负责
  • 一次性拦截器:对单个request请求负责(/api/img、/api/text)

7. 以下是加上泛型定义和其他请求的Axios封装的完整版本

拦截器的类型定义

typescript 复制代码
// /request/type.ts 文件
import type { AxiosRequestConfig } from "axios";

// 自定义拦截器中的方法的类型
interface interceptorsFunction<T = any> {
  requestOnFulfilled?: (config: any) => any; // 请求成功
  requestOnRejected?: (error: any) => any; //  请求失败
  responseOnFulfilled?: (res: T) => T; //  响应成功
  responseOnRejected?: (error: any) => any; // 响应失败
}
// 自定义拦截器类型
interface MyAxiosRequestConfig<T = any> extends AxiosRequestConfig {
  MyInterceptors?: interceptorsFunction<T>;
}

export type { MyAxiosRequestConfig };

常量的定义

- 这里的cLog是方便调试,不用的时候在这里设置为false就不打印了,打印的东西多了,看的眼花
typescript 复制代码
// /config/index.ts
export const cLog = (message:any)=>{
  // if (process.env.NODE_ENV !== 'production') return
  // if (0) return 
  console.log(message)
}

export const BASE_URL = "http://XXXXXXXXXX"
export const TIME_OUT = 10000

封装后的Axios

typescript 复制代码
// /request/index.ts
import axios from "axios";
import type { AxiosInstance } from "axios";
import { cLog } from "../config";
// 导入自定义拦截器的类型
import type { MyAxiosRequestConfig } from "./type";

class MyAxios {
  instance: AxiosInstance;
  constructor(config: MyAxiosRequestConfig) {
    this.instance = axios.create(config);
    // 全局请求拦截器
    this.instance.interceptors.request.use(
      (config) => {
        cLog("全局请求拦截器:请求成功");
        return config;
      },
      (error) => {
        cLog("全局请求拦截器:请求失败");
        return error;
      }
    );
    // 全局响应拦截器
    this.instance.interceptors.response.use(
      (res) => {
        cLog("全局响应拦截器:响应成功");
        return res.data;
      },
      (error) => {
        cLog("全局响应拦截器:响应失败");
        return error;
      }
    );
    // 请求中自定义的拦截器:实例的拦截器
    if (config.MyInterceptors) {
      this.instance.interceptors.request.use(
        config.MyInterceptors.requestOnFulfilled,
        config.MyInterceptors.requestOnRejected
      );
      this.instance.interceptors.response.use(
        config.MyInterceptors.responseOnFulfilled,
        config.MyInterceptors.responseOnRejected
      );
    }
  }

  async request<T = any>(config: MyAxiosRequestConfig<T>) {
    if (config.MyInterceptors?.requestOnFulfilled) {
      config = config.MyInterceptors.requestOnFulfilled(config);
    }
    return await this.instance
      .request<any, T>(config)
      .then((res) => {
        if (config.MyInterceptors?.responseOnFulfilled) {
          res = config.MyInterceptors.responseOnFulfilled(res);
        }
        return res;
      })
      .catch((error) => {
        if (config.MyInterceptors?.responseOnRejected)
          error = config.MyInterceptors.responseOnRejected(error);
        return error;
      });
  }
  get<T = any>(config: MyAxiosRequestConfig<T>) {
    return this.request({ ...config, method: "get" });
  }
  post<T = any>(config: MyAxiosRequestConfig<T>) {
    return this.request({ ...config, method: "post" });
  }
  delete<T = any>(config: MyAxiosRequestConfig<T>) {
    return this.request({ ...config, method: "delete" });
  }
  put<T = any>(config: MyAxiosRequestConfig<T>) {
    return this.request({ ...config, method: "put" });
  }
  patch<T = any>(config: MyAxiosRequestConfig<T>) {
    return this.request({ ...config, method: "patch" });
  }
}

export default MyAxios;

实例Axios

- myRequest1:只有全局拦截器
- myRequest2:有全局拦截器和域拦截器
typescript 复制代码
// index.ts
import MyAxios from "./request";
import { BASE_URL, TIME_OUT, cLog } from "./config";

const myRequest1 = new MyAxios({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
});

const myRequest2 = new MyAxios({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
  // 给axios发送的自定义拦截器需要执行的功能
  MyInterceptors: {
    requestOnFulfilled: (config) => {
      cLog("自定义请求拦截器:请求成功");
      return config
    },
    requestOnRejected: (error) => {
      cLog("自定义请求拦截器:请求失败");
      return error
    },
    responseOnFulfilled: (res) => {
      cLog("自定义响应拦截器:响应成功");
      return res
    },
    responseOnRejected: (error) => {
      cLog("自定义响应拦截器:响应失败");
      return error
    }
  }
})
export default { myRequest1,myRequest2 };

实际发送请求

  • 以上都可以作为固定的使用,下面的部分就是我们每次要发送请求时编写的
typescript 复制代码
// main.js
import myRequest from "../index";
import { cLog } from "../config";

myRequest.myRequest1
  .request({
    url: "XXXXX",
  })
  .then((res) => {
    cLog(res);
  });

myRequest.myRequest2
  .request({
    url: "XXXXX",
  })
  .then((res) => {
    cLog(res);
  });

myRequest.myRequest2
 // 这里的类型是服务器返回数据的类型,感觉复杂这里用any
 // 为什么这里的类型是服务器返回的数据类型?在全局拦截器中返回的是res.data
 // 如果在其他拦截器中没有对res做出改变。这里应该是AxiosResponse类型
  .request<类型>({ 
    url: "XXXXX",
    // 一次性拦截器
    MyInterceptors: {
      requestOnFulfilled: (config) => {
        cLog("一次性拦截器,请求成功拦截");
        return config;
      },
      responseOnFulfilled: (res) => {
        cLog("一次性拦截器,响应成功拦截");
        return res;
      },
      responseOnRejected: (res) => {
        cLog("一次性拦截器,响应失败拦截");
        return res;
      },
    },
  })
  .then((res) => {
    cLog(res);
  });

8. 拦截器的执行顺序

  • 只有全局拦截器
typescript 复制代码
全局请求拦截器:请求成功
全局响应拦截器:响应成功
  • 有全局拦截器和域拦截器
typescript 复制代码
自定义请求拦截器:请求成功
全局请求拦截器:请求成功
全局响应拦截器:响应成功
自定义响应拦截器:响应成功
  • 全局、域、一次性拦截器
typescript 复制代码
一次性拦截器,请求成功拦截
自定义请求拦截器:请求成功
全局请求拦截器:请求成功
全局响应拦截器:响应成功
自定义响应拦截器:响应成功
一次性拦截器,响应成功拦截

9. 目录结构

相关推荐
清灵xmf11 小时前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
Amd79419 小时前
Nuxt.js 应用中的 prepare:types 事件钩子详解
typescript·自定义·配置·nuxt·构建·钩子·类型
前端李易安2 天前
手写一个axios方法
前端·vue.js·axios
王解2 天前
Jest项目实战(2): 项目开发与测试
前端·javascript·react.js·arcgis·typescript·单元测试
鸿蒙开天组●2 天前
鸿蒙进阶篇-网格布局 Grid/GridItem(二)
前端·华为·typescript·harmonyos·grid·mate70
zhizhiqiuya2 天前
第二章 TypeScript 函数详解
前端·javascript·typescript
初遇你时动了情3 天前
react 18 react-router-dom V6 路由传参的几种方式
react.js·typescript·react-router
王解3 天前
Jest进阶知识:深入测试 React Hooks-确保自定义逻辑的可靠性
前端·javascript·react.js·typescript·单元测试·前端框架
_jiang3 天前
nestjs 入门实战最强篇
redis·typescript·nestjs
清清ww3 天前
【TS】九天学会TS语法---计划篇
前端·typescript