Axios 二次封装高级教程(含请求取消等功能)

发布于:2025-07-02 ⋅ 阅读:(16) ⋅ 点赞:(0)

Axios 二次封装高级教程(含请求取消等功能)

整理不易,收藏、点赞、关注哦!


一、总体架构设计

  • 目的:构建一套功能完善、易用且健壮的 HTTP 请求封装层

  • 核心功能

    • 请求拦截、响应拦截
    • 请求取消(防止重复请求、接口防抖)
    • 请求合并与批量处理
    • 缓存策略(内存缓存、localStorage、sessionStorage)
    • 失败重试机制
    • 上传下载进度监听
    • 状态化 hooks 集成(loading、error、data 管理)
    • 统一接口调用管理

二、Axios 实例创建与基础配置

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, Canceler } from 'axios'

const pendingRequestMap = new Map<string, Canceler>()

// 生成请求唯一key,便于取消重复请求
function getRequestKey(config: AxiosRequestConfig): string {
  const { method, url, params, data } = config
  return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
}

// 添加请求到pending队列
function addPendingRequest(config: AxiosRequestConfig) {
  const requestKey = getRequestKey(config)
  if (pendingRequestMap.has(requestKey)) {
    // 取消重复请求
    pendingRequestMap.get(requestKey)?.('取消重复请求')
  }
  config.cancelToken = new axios.CancelToken((cancel) => {
    pendingRequestMap.set(requestKey, cancel)
  })
}

// 移除请求
function removePendingRequest(config: AxiosRequestConfig) {
  const requestKey = getRequestKey(config)
  if (pendingRequestMap.has(requestKey)) {
    pendingRequestMap.delete(requestKey)
  }
}

const service: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 15000,
  headers: {
    'Content-Type': 'application/json;charset=UTF-8',
  },
})

// 请求拦截器
service.interceptors.request.use(
  (config) => {
    removePendingRequest(config) // 移除之前的请求
    addPendingRequest(config) // 添加当前请求
    // 这里可以加token等鉴权信息
    // const token = localStorage.getItem('token')
    // if (token) config.headers.Authorization = `Bearer ${token}`
    return config
  },
  (error) => Promise.reject(error)
)

// 响应拦截器
service.interceptors.response.use(
  (response: AxiosResponse) => {
    removePendingRequest(response.config)
    // 统一处理状态码
    const { data } = response
    if (data.code !== 0) {
      // 自定义错误处理
      return Promise.reject(new Error(data.message || 'Error'))
    }
    return data
  },
  (error) => {
    if (axios.isCancel(error)) {
      console.warn('请求被取消:', error.message)
      return Promise.reject({ canceled: true })
    }
    return Promise.reject(error)
  }
)

export default service

三、请求取消与防抖(防重复请求)

  • 场景:用户快速重复点击按钮,多次发送相同请求
  • 解决:通过cancelToken取消前一个重复请求,保证接口调用稳定

核心逻辑已在上面pendingRequestMap实现,结合唯一请求Key判断。


四、多请求合并(批量接口请求)

/**
 * 批量请求示例
 * @param urls 请求地址数组
 */
function batchRequest(urls: string[]) {
  const requests = urls.map((url) => service.get(url))
  return axios.all(requests).then(axios.spread((...responses) => responses))
}

// 使用
batchRequest(['/api/a', '/api/b', '/api/c']).then(([resA, resB, resC]) => {
  console.log(resA, resB, resC)
})

五、缓存策略(内存缓存 + 本地缓存)

const cacheMap = new Map<string, any>()

// 简单缓存示例
function requestWithCache<T = any>(config: AxiosRequestConfig, useCache = true): Promise<T> {
  const key = getRequestKey(config)
  if (useCache && cacheMap.has(key)) {
    return Promise.resolve(cacheMap.get(key))
  }
  return service.request<T>(config).then((res) => {
    cacheMap.set(key, res)
    return res
  })
}
  • 可根据业务需求,扩展使用localStoragesessionStorage缓存,并设置过期时间。

六、自动失败重试机制

interface RetryConfig extends AxiosRequestConfig {
  retry?: number // 重试次数
  retryDelay?: number // 重试间隔ms
}

function retryRequest(config: RetryConfig): Promise<any> {
  const { retry = 3, retryDelay = 1000 } = config
  let currentRetry = 0

  const request = (): Promise<any> => {
    return service.request(config).catch((error) => {
      if (currentRetry < retry) {
        currentRetry++
        return new Promise((resolve) => setTimeout(resolve, retryDelay)).then(request)
      }
      return Promise.reject(error)
    })
  }

  return request()
}
  • 可以在请求层或调用层根据需求灵活调用。

七、上传和下载进度监听

// 上传进度
function uploadFile(url: string, file: File, onProgress: (percent: number) => void) {
  const formData = new FormData()
  formData.append('file', file)
  return service.post(url, formData, {
    headers: { 'Content-Type': 'multipart/form-data' },
    onUploadProgress: (progressEvent) => {
      const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)
      onProgress(percent)
    },
  })
}

// 下载进度
function downloadFile(url: string, onProgress: (percent: number) => void) {
  return service.get(url, {
    responseType: 'blob',
    onDownloadProgress: (progressEvent) => {
      const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)
      onProgress(percent)
    },
  })
}

八、封装状态式 Hook(结合 Vue 3 Composition API)

import { ref } from 'vue'

interface UseRequestOptions {
  manual?: boolean // 是否手动调用
  retry?: number
  retryDelay?: number
}

export function useRequest<T = any>(
  requestFn: (...args: any[]) => Promise<T>,
  options: UseRequestOptions = {}
) {
  const data = ref<T | null>(null)
  const loading = ref(false)
  const error = ref<Error | null>(null)

  const fetch = async (...args: any[]) => {
    loading.value = true
    error.value = null
    try {
      data.value = await requestFn(...args)
      loading.value = false
      return data.value
    } catch (err: any) {
      error.value = err
      loading.value = false
      throw err
    }
  }

  if (!options.manual) {
    fetch()
  }

  return { data, loading, error, fetch }
}
  • 使用示例:
import { useRequest } from './useRequest'
import service from './axios'

const { data, loading, error, fetch } = useRequest(() => service.get('/api/user'))

九、文件上传并发送给后端(结合FileReader)

function uploadFileWithPreview(file: File, url: string) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(file) // 读取文件为base64编码
    reader.onload = () => {
      // 可以先预览file的base64,也可以直接上传二进制
      const formData = new FormData()
      formData.append('file', file) // 直接上传文件
      service
        .post(url, formData, {
          headers: { 'Content-Type': 'multipart/form-data' },
        })
        .then(resolve)
        .catch(reject)
    }
    reader.onerror = (err) => reject(err)
  })
}

十、总结

  1. 请求取消通过唯一请求key + CancelToken实现,防止重复请求。
  2. 多请求合并使用axios.allaxios.spread实现批量请求并行。
  3. 缓存策略基于请求key设计内存或本地缓存,提升请求效率。
  4. 自动重试可根据失败情况自动重试,增强请求鲁棒性。
  5. 上传/下载进度监听灵活支持文件上传和下载的进度反馈。
  6. 状态式 Hook结合 Vue 3 Composition API,实现简洁易用的请求状态管理。
  7. 文件上传可结合 FileReader 实现预览和上传双功能。

网站公告

今日签到

点亮在社区的每一天
去签到