Vue 4 中自定义 Axios 接口

在 Vue 4 项目中,合理封装 Axios 接口可以显著提高代码的可维护性和复用性。下面我将提供一个完整的自定义 Axios 接口实现方案,包括基础配置、拦截器封装、接口管理和错误处理等核心功能。

一、基础配置与安装

1. 安装 Axios

首先需要在项目中安装 Axios 依赖:

npm install axios
# 或
yarn add axios

2. 创建基础 Axios 实例

src/utils/request.js 中创建基础 Axios 实例:

import axios from 'axios'

// 创建 axios 实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // 从环境变量获取基础URL
  timeout: 10000, // 请求超时时间
  withCredentials: true // 允许跨域携带cookie
})

export default service

二、拦截器封装

1. 请求拦截器

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 在发送请求之前做些什么
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    
    // 添加自定义请求头
    config.headers['X-Request-Id'] = generateUUID()
    config.headers['X-Client-Type'] = 'web'
    
    return config
  },
  error => {
    // 对请求错误做些什么
    return Promise.reject(error)
  }
)

2. 响应拦截器

// 响应拦截器
service.interceptors.response.use(
  response => {
    // 对响应数据做点什么
    const res = response.data
    
    // 自定义业务状态码处理
    if (res.code !== 200) {
      // 处理业务错误
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    // 对响应错误做点什么
    if (error.response) {
      // HTTP状态码处理
      switch (error.response.status) {
        case 401:
          // 处理未授权
          break
        case 403:
          // 处理禁止访问
          break
        case 404:
          // 处理资源不存在
          break
        case 500:
          // 处理服务器错误
          break
        default:
          // 其他错误
      }
    }
    return Promise.reject(error)
  }
)

三、接口统一管理

1. 创建 API 管理文件

src/api 目录下创建模块化的接口文件,例如 user.js

import request from '@/utils/request'

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

// 获取用户信息
export function getInfo(userId) {
  return request({
    url: `/user/${userId}`,
    method: 'get'
  })
}

// 更新用户信息
export function updateUser(userId, data) {
  return request({
    url: `/user/${userId}`,
    method: 'put',
    data
  })
}

// 删除用户
export function deleteUser(userId) {
  return request({
    url: `/user/${userId}`,
    method: 'delete'
  })
}

2. 文件上传接口示例

// 文件上传
export function uploadFile(file) {
  const formData = new FormData()
  formData.append('file', file)
  
  return request({
    url: '/upload',
    method: 'post',
    data: formData,
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  })
}

四、在 Vue 4 组件中使用

1. 基本使用示例

<template>
  <div>
    <button @click="fetchUserData">获取用户数据</button>
    <div v-if="loading">加载中...</div>
    <div v-else>{{ userData }}</div>
  </div>
</template>

<script>
import { getInfo } from '@/api/user'

export default {
  data() {
    return {
      userData: null,
      loading: false
    }
  },
  methods: {
    async fetchUserData() {
      this.loading = true
      try {
        const response = await getInfo(123)
        this.userData = response
      } catch (error) {
        console.error('获取用户数据失败:', error)
      } finally {
        this.loading = false
      }
    }
  }
}
</script>

2. 组合式 API 使用示例

<template>
  <div>
    <button @click="login">登录</button>
    <div>{{ userInfo }}</div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { login } from '@/api/user'

const userInfo = ref(null)

const login = async () => {
  try {
    const response = await login({
      username: 'admin',
      password: '123456'
    })
    userInfo.value = response
  } catch (error) {
    console.error('登录失败:', error)
  }
}
</script>

五、高级功能实现

1. 请求取消功能

import axios from 'axios'

const CancelToken = axios.CancelToken
let cancel

// 获取数据
export function getData(params) {
  // 取消之前的请求
  if (cancel) cancel()
  
  return request({
    url: '/data',
    method: 'get',
    params,
    cancelToken: new CancelToken(function executor(c) {
      cancel = c
    })
  })
}

2. 请求重试机制

// 在响应拦截器中实现
service.interceptors.response.use(
  response => response,
  async error => {
    const config = error.config
    
    // 设置重试次数和当前重试次数
    config.__retryCount = config.__retryCount || 0
    const retryLimit = 3
    
    // 检查是否达到重试限制
    if (config.__retryCount >= retryLimit) {
      return Promise.reject(error)
    }
    
    // 增加重试计数器
    config.__retryCount += 1
    
    // 创建新的Promise来处理指数退避
    const delay = new Promise(resolve => {
      setTimeout(() => {
        resolve()
      }, 1000 * config.__retryCount)
    })
    
    // 延迟后重试请求
    await delay
    return service(config)
  }
)

3. 请求缓存实现

const cacheMap = new Map()

export function getDataWithCache(params) {
  const cacheKey = JSON.stringify(params)
  
  if (cacheMap.has(cacheKey)) {
    return Promise.resolve(cacheMap.get(cacheKey))
  }
  
  return request({
    url: '/data',
    method: 'get',
    params
  }).then(response => {
    cacheMap.set(cacheKey, response)
    return response
  })
}

六、全局配置与 TypeScript 支持

1. 全局配置示例

main.js 中:

import { createApp } from 'vue'
import App from './App.vue'
import axios from 'axios'
import VueAxios from 'vue-axios'

const app = createApp(App)

// 全局配置
axios.defaults.baseURL = process.env.VUE_APP_API_BASE_URL
axios.defaults.timeout = 10000
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'

app.use(VueAxios, axios)
app.mount('#app')

2. TypeScript 类型定义

创建 src/types/axios.d.ts

import axios from 'axios'

declare module 'axios' {
  interface AxiosRequestConfig {
    // 自定义配置项
    retry?: number
    retryDelay?: number
    cache?: boolean
  }
  
  interface AxiosResponse<T = any> {
    // 自定义响应结构
    code: number
    message: string
    data: T
    timestamp: number
  }
}

七、跨域解决方案

1. 开发环境代理配置

vue.config.js 中:

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: ' http://backend.example.com',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        },
        headers: {
          'X-Proxy-Header': 'vue-dev-server'
        }
      }
    }
  }
}

2. 生产环境 CORS 配置

确保后端服务器配置了正确的 CORS 头:

// Node.js Express 示例
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  res.header('Access-Control-Allow-Credentials', 'true')
  
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200)
  }
  
  next()
})

八、最佳建议

  1. 模块化组织:按业务模块拆分 API 文件,保持代码结构清晰

  2. 统一错误处理:在拦截器中实现全局错误处理,避免重复代码

  3. 合理使用拦截器:请求拦截器适合添加认证信息,响应拦截器适合处理通用业务逻辑

  4. 类型安全:使用 TypeScript 增强代码的可维护性和开发体验

  5. 性能优化:对于频繁请求且数据变化不频繁的接口,考虑实现缓存机制

  6. 安全考虑:敏感信息如 token 应存储在安全的地方(如 HttpOnly Cookie)

  7. 文档化:为每个接口添加清晰的注释说明参数和返回值

通过以上方案,您可以在 Vue 4 项目中实现一个功能完善、易于维护的自定义 Axios 接口系统。根据实际项目需求,您可以灵活调整各部分实现细节。


Vue 4 中自定义 Axios 接口
https://uniomo.com/archives/vue-4-zhong-zi-ding-yi-axios-jie-kou
作者
雨落秋垣
发布于
2025年09月21日
许可协议