Axios的封装思路与技巧

Axios的封装思路与技巧

提示:文中使用的为ts代码,对ts不熟悉的同学可以删除所有类型降级为js代码,不影响使用

前言

项目中或多或少会有一些需要接口发送请求的需求,与其复制粘贴别人在业务中对请求方法的使用,不如自己花点时间研究项目中请求方法的实现,这样在处理请求出现的问题时能够更好的定位问题原因。本文循序渐进的介绍了如何对axios进行封装实现自己项目中的请求方法,希望各位同学在阅读后能有一定的体会,如有问题还请大家在评论区指正。

1.创建axios实例

创建一个axios实例,在这里为实例进行一些配置(如超时时间)。但是要注意,不要在此处配置一些动态的属性,如headers中的token,具体的原因我们会在后面提起

javascript 复制代码
import axios from 'axios'
const instance = axios.create({
  timeout: 1000 * 120, // 超时时间120s
})

为实例配置拦截器(请求拦截器,响应拦截器)

可能有些同学对axios不太熟悉,不了解请求拦截器和响应拦截器的作用,这里会简单介绍一下

请求拦截器:在请求发送前进行拦截,或者对请求错误进行拦截

javascript 复制代码
instance.interceptors.request.use(
  config => {
    // config为AxiosRequestConfig的一个实例,它是包含请求配置参数的对象
    // 在这里可以在请求发送前做一些处理,如向config实例中添加属性,取消请求,设置loading等  
    return config
  },
  // 这里是请求报错时的拦截方法,这里直接返回一个状态为reject的promise
  // 实际测试时,即使前端请求报错并且未到达后端,也没有触发这里的钩子函数
  error => Promise.reject(error),
)

响应拦截器:在响应被.then.catch处理前拦截

javascript 复制代码
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,因为它的主要作用是做为一个底层的类让其他类去继承,在这里我们提供一些属性的配置,以及一些基础的请求方法

typescript 复制代码
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类,代表用户相关的请求

typescript 复制代码
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

文件创建好了之后我们就可以引用到具体项目中使用了

php 复制代码
// 可以在这里传入baseUrl,这也是基于类封装的好处,我们可以实例化多个user并使用不同的baseUrl
const userInstance = new User()
const res = await userInstance.login({
  username: 'xxx',
  password: 'xxx'
})
相关推荐
蓝天星空28 分钟前
html生成注册与登录代码
javascript·css·html
宜昌李国勇34 分钟前
`http_port_t
android·前端
我家猫叫佩奇38 分钟前
React项目eslint8 升级到 9记录
前端
API_Zevin1 小时前
如何优化亚马逊广告以提高ROI?
大数据·开发语言·前端·后端·爬虫·python·学习
野槐1 小时前
CSS进阶和SASS
前端·less·scss
玩具工匠2 小时前
字玩FontPlayer开发笔记3 性能优化 大量canvas渲染卡顿问题
前端·javascript·vue.js·笔记·elementui·typescript
CodeClimb2 小时前
【华为OD-E卷 - 服务失效判断 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
CodeClimb2 小时前
【华为OD-E卷 - 九宫格按键输入 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
m0_748248772 小时前
YOLOv5部署到web端(flask+js简单易懂)
前端·yolo·flask
qwaesrdt32022 小时前
【如何使用大语言模型(LLMs)高效总结多文档内容】
前端