Axios的封装思路与技巧
Axios的封装思路与技巧
提示:文中使用的为ts代码,对ts不熟悉的同学可以删除所有类型降级为js代码,不影响使用
前言
项目中或多或少会有一些需要接口发送请求的需求,与其复制粘贴别人在业务中对请求方法的使用,不如自己花点时间研究项目中请求方法的实现,这样在处理请求出现的问题时能够更好的定位问题原因。本文循序渐进的介绍了如何对axios进行封装实现自己项目中的请求方法,希望各位同学在阅读后能有一定的体会,如有问题还请大家在评论区指正。
1.创建axios实例
创建一个axios
实例,在这里为实例进行一些配置(如超时时间)。但是要注意,不要在此处配置一些动态的属性,如headers
中的token
,具体的原因我们会在后面提起
import axios from 'axios'
const instance = axios.create({
timeout: 1000 * 120, // 超时时间120s
})
为实例配置拦截器(请求拦截器,响应拦截器)
可能有些同学对axios
不太熟悉,不了解请求拦截器和响应拦截器的作用,这里会简单介绍一下
请求拦截器:在请求发送前进行拦截,或者对请求错误进行拦截
instance.interceptors.request.use(
config => {
// config为AxiosRequestConfig的一个实例,它是包含请求配置参数的对象
// 在这里可以在请求发送前做一些处理,如向config实例中添加属性,取消请求,设置loading等
return config
},
// 这里是请求报错时的拦截方法,这里直接返回一个状态为reject的promise
// 实际测试时,即使前端请求报错并且未到达后端,也没有触发这里的钩子函数
error => Promise.reject(error),
)
响应拦截器:在响应被.then
或.catch
处理前拦截
instance.interceptors.response.use(
response => {
// 响应成功的场景
// 在这里可以关闭loading或者对响应的返参对象response进行处理
return response
},
error => {
// 响应失败的场景
// http状态码不为2xx时就会进入,根据项目要求处理接口401,404,500等情况
// 返回的promise也可以根据项目要求进行修改
return Promise.reject(error)
},
)
这样我们就创建了一个可用的axios
实例,对于实例的一些其他配置可以参考axios官网
2.创建Abstract类进一步封装
在创建了一个axios
实例之后,我们就可以使用它去发送请求了,但是出于减少重复代码的目的,我们不在业务代码中直接使用axios
实例去发送各种请求,而是选择去做进一步的封装让整体的代码更加简洁
通常来说,我在项目中更喜欢用面向对象的方式去对axios
做进一步的封装,使用这种方式的优点会在后面进行说明 创建一个类,起名可以按自己的喜好来,这里我写的是Abstract
,因为它的主要作用是做为一个底层的类让其他类去继承,在这里我们提供一些属性的配置,以及一些基础的请求方法
import axios from './axios'
import type { AxiosRequest, CustomResponse } from './types/index'
class Abstract {
// 配置接口的baseUrl,这里用的是vite环境变量,可以根据需求自行修改
protected baseURL: string = import.meta.env.VITE_BASEURL
// 配置接口的请求头,这里仅简单配置一下
protected headers: object = {
'Content-Type': 'application/json;charset=UTF-8',
}
// 提供类的构造器,可以在这里修改一些基础参数如baseUrl
constructor(baseURL?: string) {
this.baseURL = baseURL ?? this.baseURL
}
// 重点!发起请求的方法
// 这里的T是ts中泛型的用法,主要用于控制接口返回的类型,不熟悉ts的同学可以略过
private apiAxios<T = any>({
baseURL = this.baseURL,
headers = this.headers,
method,
url,
data,
params,
responseType
}: AxiosRequest): Promise<CustomResponse<T>> {
// 在这里加上请求头的好处在于,每次请求时都会动态读取存储的token值
// 正如前面所说的,不要在创建axios实例时在header上配置token是因为,浏览器除非刷新,否则只会创建一次axios实例,它的header上的token的值不会发生变化,如果涉及到用户退出等清除token的操作,下次登录时获得的新token不会被使用
Object.assign(headers, {
// 根据情况使用localStorage或sessionStorage
token: localStorage.getItem('token')
})
return new Promise((resolve, reject) => {
axios({
baseURL,
headers,
method,
data,
url,
params,
responseType,
})
.then(res => {
// 在这里处理http2xx的接口,根据业务的需要进行一些处理,返回一个成功的promise
// 这里仅为演示,直接返回了原始的res
resolve(res)
})
.catch(err => {
// 在这里处理http不成功的状态,并根据业务的需要进行一个处理,返回一个失败的promise
reject(err)
})
})
}
// 通常我还会在基础类上封装一些现成的请求方法,如Get Post等,可以根据自己的需要封装其他的请求方法
protected getReq<T = any>({ baseURL, headers, url, data, params, responseType, messageType }: AxiosRequest) {
return this.apiAxios<T>({
baseURL,
headers,
method: 'GET',
url,
data,
params,
responseType,
messageType,
})
}
protected postReq<T = any>({ baseURL, headers, url, data, params, responseType, messageType }: AxiosRequest) {
return this.apiAxios<T>({
baseURL,
headers,
method: 'POST',
url,
data,
params,
responseType,
messageType,
})
}
3.继承Abstract类实现业务相关的请求类
这样,我们就成功封装了一个Axios
的基础类,接下来可以创建一个新的业务类去继承它并使用。这里我们创建一个User
类,代表用户相关的请求
import Abstract from '@/api/abstract'
class User extends Abstract {
constructor(baseUrl?: string) {
super(baseUrl)
}
// post请求
login(data: unknown) {
return this.postReq({
data,
url: 'back/v1/user/login',
})
}
// get请求
getUser(param: { id: string })
return this.getReq({
param,
url: 'back/v1/user/getUser'
})
}
// 需要修改请求头的Content-Type,如表单上传
saveUser(data: any) {
const formData = new FormData()
Object.keys(data).forEach(key => {
formData.append('file', data[key])
})
return this.postReq({
data,
headers: {
'Content-Type': 'multipart/form-data',
},
url: 'back/v1/user/saveUser',
})
}
export default User
文件创建好了之后我们就可以引用到具体项目中使用了
// 可以在这里传入baseUrl,这也是基于类封装的好处,我们可以实例化多个user并使用不同的baseUrl
const userInstance = new User()
const res = await userInstance.login({
username: 'xxx',
password: 'xxx'
})
作者:kamesan
来源:juejin.cn/post/7264749103125184527