Axios 封装的作用

文章类型:Vue

发布者:hp

发布时间:2026-07-06

在实际项目开发中,直接使用 axios 会导致大量重复代码和分散的配置。通过二次封装,可以统一管理请求配置、错误处理、权限验证等通用逻辑,大幅提升开发效率和代码质量。

一、创建 Axios 实例


首先创建一个配置好的 axios 实例,统一管理基础配置:

// utils/request.js
import axios from 'axios'

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // 统一的基础URL
  timeout: 15000, // 请求超时时间
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  }
})

二、请求拦截器(Request Interceptor)


在请求发送前进行统一处理,如添加 token、参数处理等:

service.interceptors.request.use(
  config => {
    // 1. 添加 token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    
    // 2. 添加时间戳防止缓存
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        _t: Date.now()
      }
    }
    
    // 3. 显示 loading(可选)
    // showLoading()
    
    return config
  },
  error => {
    console.error('请求错误:', error)
    return Promise.reject(error)
  }
)

三、响应拦截器(Response Interceptor)


统一处理响应数据和错误:

service.interceptors.response.use(
  response => {
    // hideLoading()
    
    const res = response.data
    
    // 根据后端返回的 code 判断
    if (res.code !== 200) {
      // 处理业务错误
      if (res.code === 401) {
        // token 过期,跳转登录
        localStorage.removeItem('token')
        router.push('/login')
      }
      
      // 错误提示
      Message.error(res.message || '请求失败')
      return Promise.reject(new Error(res.message || 'Error'))
    }
    
    return res.data // 只返回数据部分
  },
  error => {
    // hideLoading()
    
    // 处理 HTTP 错误
    let message = '网络错误'
    if (error.response) {
      switch (error.response.status) {
        case 400:
          message = '请求参数错误'
          break
        case 401:
          message = '未授权,请重新登录'
          localStorage.removeItem('token')
          router.push('/login')
          break
        case 403:
          message = '拒绝访问'
          break
        case 404:
          message = '请求地址不存在'
          break
        case 500:
          message = '服务器错误'
          break
        case 502:
          message = '网关错误'
          break
        case 503:
          message = '服务不可用'
          break
        case 504:
          message = '网关超时'
          break
        default:
          message = `连接错误${error.response.status}`
      }
    } else if (error.message.includes('timeout')) {
      message = '请求超时'
    } else if (error.message.includes('Network Error')) {
      message = '网络连接异常'
    }
    
    Message.error(message)
    return Promise.reject(error)
  }
)

export default service

四、二次封装的主要作用


统一配置管理

统一的 baseURL、超时时间、请求头设置,避免分散配置


统一认证处理

自动添加 token,token 过期自动跳转登录,无需手动处理


统一错误处理

集中处理 HTTP 状态码错误,统一错误提示


数据转换

统一处理返回数据格式,只返回需要的数据部分


⏳ Loading 管理

自动显示/隐藏 loading,避免重复的控制代码


请求重试

可配置请求失败自动重试机制

五、API 模块化管理


将不同业务的 API 分模块管理,保持代码清晰:

// api/user.js
import request from '@/utils/request'

// 登录
export function login(data) {
  return request({
    url: '/user/login',
    method: 'post',
    data
  })
}

// 获取用户信息
export function getUserInfo() {
  return request({
    url: '/user/info',
    method: 'get'
  })
}

// 更新用户信息
export function updateUserInfo(data) {
  return request({
    url: '/user/update',
    method: 'put',
    data
  })
}

六、组件中使用

使用示例:

import { login, getUserInfo } from '@/api/user'

export default {
  data() {
    return {
      userInfo: null
    }
  },
  async mounted() {
    try {
      // 直接调用,无需关心 token、错误处理等
      const data = await getUserInfo()
      this.userInfo = data
    } catch (error) {
      // 错误已经在拦截器中处理
      // 这里可以做额外的业务处理
    }
  },
  methods: {
    async handleLogin() {
      try {
        const res = await login({
          username: this.username,
          password: this.password
        })
        localStorage.setItem('token', res.token)
        this.$router.push('/home')
      } catch (error) {
        // 已统一处理
      }
    }
  }
}

七、高级功能(可选)

1. 取消重复请求

// 防止重复请求
const pending = new Map()

const addPending = (config) => {
  const url = [config.method, config.url].join('&')
  config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
    if (!pending.has(url)) {
      pending.set(url, cancel)
    }
  })
}

const removePending = (config) => {
  const url = [config.method, config.url].join('&')
  if (pending.has(url)) {
    const cancel = pending.get(url)
    cancel(url)
    pending.delete(url)
  }
}

// 在请求拦截器中使用
service.interceptors.request.use(config => {
  removePending(config) // 检查是否存在重复请求
  addPending(config) // 添加当前请求到 pending
  return config
})

2. 请求重试机制

// 在响应拦截器的错误处理中添加重试
service.interceptors.response.use(
  response => response,
  error => {
    const config = error.config
    
    // 如果没有配置重试或已达到最大重试次数
    if (!config || !config.retry) {
      return Promise.reject(error)
    }
    
    // 设置重试计数
    config.__retryCount = config.__retryCount || 0
    
    // 检查是否已达到最大重试次数
    if (config.__retryCount >= config.retry) {
      return Promise.reject(error)
    }
    
    // 增加重试计数
    config.__retryCount += 1
    
    // 创建延迟重试的 Promise
    const backoff = new Promise(resolve => {
      setTimeout(() => {
        resolve()
      }, config.retryDelay || 1000)
    })
    
    // 返回重试的 Promise
    return backoff.then(() => service(config))
  }
)

// 使用时配置重试
request({
  url: '/api/data',
  method: 'get',
  retry: 3, // 重试 3 次
  retryDelay: 1000 // 每次重试延迟 1 秒
})

3. 文件上传封装

// api/upload.js
import request from '@/utils/request'

export function uploadFile(file, onProgress) {
  const formData = new FormData()
  formData.append('file', file)
  
  return request({
    url: '/upload',
    method: 'post',
    data: formData,
    headers: {
      'Content-Type': 'multipart/form-data'
    },
    onUploadProgress: (progressEvent) => {
      if (onProgress) {
        const percent = Math.round(
          (progressEvent.loaded * 100) / progressEvent.total
        )
        onProgress(percent)
      }
    }
  })
}

八、完整目录结构


src/
├── utils/
│   └── request.js          # axios 封装
├── api/
│   ├── user.js             # 用户相关 API
│   ├── article.js          # 文章相关 API
│   ├── upload.js           # 上传相关 API
│   └── index.js            # API 统一导出
└── components/
    └── YourComponent.vue   # 使用 API

九、总结


Axios 二次封装带来的核心价值:

  • 代码复用避免在每个请求中重复编写相同的配置和错误处理代码
  • 统一管理集中处理 token、错误、loading 等通用逻辑
  • 易于维护修改一处即可全局生效,降低维护成本
  • 提升效率开发时只需关注业务逻辑,无需处理通用问题
  • 代码简洁组件中的请求代码更加清晰易读
  • 团队协作统一的代码风格和错误处理方式



评论
0条评论遵守法律,文明用语,共同建设文明评论区

暂无评论,快来发表第一条评论吧~