注册
web

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

0 个评论

要回复文章请先登录注册