TypeScript封装axios【包括详细的请求接口和设置】
问题描述现在我们的项目需要升级,技术栈为Vue3+TypeScript。所以,现在我需要使用TS对axios进行重新封装解决过程1.安装axios使用npm安装axios依赖npm install axios2.实现基本封装首先导入axios创造构造器设置request其中可能出现的问题Cannot find module ‘axios;‘ or its corresponding type de
问题描述
现在我们的项目需要升级,技术栈为Vue3
+TypeScript
。所以,现在我需要使用TS对axios进行重新封装
封装目标
- 调用接口时,只需要确定url和method
- 出现多个请求地址时,通过修改.env类似文件即可,无需修改源代码
封装过程
1.安装axios
使用npm安装axios依赖
npm install axios
2.实现基本封装
- 首先导入axios
- 创造构造器
- 设置request
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
class Request {
private instance: AxiosInstance | undefined
constructor(config: AxiosRequestConfig) {
this.instance = axios.create(config)
}
request(config: AxiosRequestConfig): Promise<AxiosResponse> {
return new Promise<AxiosResponse>((resolve, reject) => {
this.instance?.request(config)
.then((res) => {
resolve(res)
})
.catch((err) => {
reject(err)
})
})
}
}
将其封装为一个类,而不是一个函数。
因为类可以创建多个实例,也就是说可以访问完全不同的服务器的接口。
其中可能出现的问题
Cannot find module ‘axios;‘ or its corresponding type declarations.ts(2307)
3.创建实例
我想通过不同的运行环境创建不同的实例,所以设置如下
创建.env
文件
根据vue-cli 模式与环境变量官方写到,可根据env后指定mode即可创建不同的请求路径
创建.env.development
——用于开发环境
NODE_ENV = development
VUE_APP_BASE_URL = "http://你的开发请求的地址"
创建.env.production
——用于正式部署环境
NODE_ENV = production
VUE_APP_BASE_URL = "http://正式部署时请求的地址"
创建实例
因为我根据不同环境去设置变量时候,地址名字都是VUE_APP_BASE_RUL
所以,可以根据此去创建axios请求的baseUrl
import Request from "./request";
/**
* process.env.VUE_APP_BASE_URL 根据NODE_ENV变化而变化
*/
const web: Request = new Request({
baseURL: process.env.VUE_APP_BASE_URL,
})
export default web
现在尝试在main.ts
中调用一个接口
import web from './utils/request/index'
web.request({
url: '/login',
})
.then((res) => {
console.log(res);
})
4.添加响应和请求拦截器
现在对request.ts
进行优化升级(添加拦截器+泛型——复用性)
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
// import type { AxiosInstance , AxiosResponse } from 'axios';
class Request {
private instance: AxiosInstance | undefined
constructor(requeseConfig: AxiosRequestConfig) {
this.instance = axios.create(requeseConfig)
// 全局请求拦截
this.instance.interceptors.request.use(
(config) => {
console.log("全局请求拦截的", config);
return config
},
(error) => {
console.log("全局请求拦截失败", error);
},
)
// 全局响应拦截
this.instance.interceptors.response.use(
(res) => {
// res 为AxiosResponse 类型,含有conig\data\headers\request\status\statusText属性
console.log("全局响应拦截的", res);
return res.data
// 只需要返回data即可
},
(error) => {
console.log("全局响应失败拦截");
console.log(error.request);
console.log(error.response);
return error
},
)
}
request<T>(config: AxiosRequestConfig<T>): Promise<T> {
return new Promise<T>((resolve, reject) => {
/* eslint-disable */
this.instance?.request<any, T>(config)
.then((res) => {
resolve(res)
})
.catch((err) => {
reject(err)
})
})
}
}
export default Request
再次在main.ts中调用
web.request({
url: '/login',
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
})
现在的结果
5.在请求头中添加token
在创建的index.ts
中,添加token
import Request from "./request";
/**
* process.env.VUE_APP_BASE_URL 根据NODE_ENV变化而变化
*/
/* eslint-disable */
const token = String(window.localStorage.getItem('token'))
const web: Request = new Request({
baseURL: process.env.VUE_APP_BASE_URL,
timeout: process.env.VUE_APP_TIME_OUT,
headers: {
'Content-Type': 'application/json',
'Accept': "application/json",
'Authorization': token,
},
})
export default web
现在的请求结果
现在请求的接口,带上了token,便于后端进行权限控制
6.封装GET,POST方法
get(url: string) {
return new Promise((resolve, reject) => {
this.instance?.post(url)
.then((res) => {
resolve(res.data)
})
.catch((err) => {
reject(err.data)
})
})
}
post(url: string, data = {}) {
return new Promise((resolve, reject) => {
this.instance?.post(url, data)
.then((res) => {
resolve(res.data)
})
.catch((err) => {
reject(err.data)
})
})
}
就是在这里,我自闭了好久根本搞不懂别人是怎么做的。
因为一开始我根本对axios和ts都没有一个理解和认知,所以对于更加抽象的封装就显得无能为力了。
然后我就开始尝试看之前我们项目的代码结合其他开源项目的操作,就想到直接传url
和data
并给data一个初始值。
7.定义对应后端的Ts的接口
因为我希望对项目进行模块化的封装,于是创建了专门封装接口的api文件夹,文件夹下根据模块新建文件夹。
这里以user模块下的login为例
在api模块下,我创建了models.ts
,里面存放所有与后端对应接口的类型接口
例如:
后端的登录接口可以接受参数login包括
- 邮箱:string
- 密码:string
- 验证码:string
- 时间错:string
于是在userModels.ts
下定义接口如下
// 登录接口
export interface login {
accountEmail: string,
accountPassword: string,
code: string,
data: string,
}
8.实现接口的封装
同样的继续创建login.ts
文件,去实现接口
代码如下:
import web from "@/utils/request";
import { login } from "../models";
export default function login(requesrData?: login) {
const responesData = web.post('/login', requesrData)
return responesData;
}
- 因为后端可以不传入任何参数所以使用
?:
去定义类型- 当有参数时,遵守login接口类型
- 当没有参数时,直接进入即可
9.开始调用接口
在对应的页面进行调用接口
<template>
<button @click="login">来吧,试试一个接口看看</button>
</template>
<script setup lang='ts'>
import login from '@/api/user/login';
const a = 1;
console.log(a);
const login= async () => {
await login({
accountEmail: "xx@qq.com",
accountPassword: "xxxxxxxxxxxxxx",
code: "2xl4",
data: "1647152454419",
}
</script>
<style scoped>
</style>
若是不满足定义的类型,则会报错
但是!!!这里注意的是
虽然静态报错,但是依旧可以运行成功!!!,暂时还没有找到解决办法阻断运行
总结
运行成功~
成功的那一刻是喜悦的!!!毕竟这算是自己独立封装出来的
虽然可能一些可拓展性存在缺陷,但终究结果也是可以接受的。这次用TS去封装axios有点快要自闭的感觉,自己对这些东西理解不深,然后看那些为了可扩展性和高复用性进行高度抽象的封装,就有些不知所措了,而且在这个过程中,自己没有及时的去真正尝试去理解。
而是只有当真正走投无路的时候,才开始去尝试其他的方法,有点过度依赖博客的感觉!
最后成功其实是借鉴了一些类似Vue3+TS的开源项目的封装思想。
所以这也提醒了我一个问题,当在百度中找不到你想要答案的时候,去看看其他人同类型的项目,获取就会豁然开朗
毕竟,一个程序员都是从借鉴学习开始的!
更多推荐
所有评论(0)