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. 目录结构

相关推荐
jstart千语31 分钟前
【vue3学习】vue3入门
前端·javascript·vue.js·typescript·vue
struggle20256 小时前
RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上
数据库·typescript·neo4j
狂炫一碗大米饭12 小时前
如何在 TypeScript 中使用类型保护
typescript
码农之王17 小时前
(一)TypeScript概述和环境搭建
前端·后端·typescript
蚂小蚁21 小时前
从DeepSeek翻车案例看TypeScript类型体操的深层挑战
typescript·deepseek·trae
在人间耕耘2 天前
鸿蒙应用开发:WebSocket 使用示例
typescript
在人间耕耘2 天前
uni-app/uniappx 中调用鸿蒙原生扫码能力的实践
typescript
海的诗篇_2 天前
前端开发面试题总结-JavaScript篇(二)
开发语言·前端·javascript·typescript
dancing9992 天前
cocos3.X的oops框架oops-plugin-excel-to-json改进兼容多表单导出功能
前端·javascript·typescript·游戏程序
yinke小琪3 天前
快速开始 - TypeScript 入门指南
前端·typescript