vue对axios的封装和使用

在 Vue 项目中,使用 axios 进行 HTTP 请求是非常常见的做法。为了提高代码的可维护性、统一错误处理和请求拦截/响应拦截逻辑,对axios进行封装使用。

一、基础封装(适用于 Vue 2 / Vue 3)

1. 安装 axios

bash 复制代码
npm install axios

2. 创建封装文件:src/utils/request.js

js 复制代码
import axios from 'axios'
import { Message } from 'element-ui' // 或你使用的 UI 库

// 创建 axios 实例
const service = axios.create({
  baseURL: process.env.VUE_APP_API, // 设置默认 base URL(来自 .env)
  timeout: 5000, // 超时时间
  withCredentials: false // 是否携带 cookie
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data

    if (res.code !== 200) {
      Message.error(res.message || 'Error')

      if (res.code === 401) {
        // 处理 token 失效
        localStorage.removeItem('token')
        window.location.href = '/login'
      }

      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    Message.error('网络异常,请检查网络连接')
    return Promise.reject(error)
  }
)

export default service

二、使用封装后的 axios

1. 在组件中直接调用

js 复制代码
import request from '@/utils/request'

export default {
  methods: {
    async fetchData() {
      try {
        const res = await request.get('/api/data')
        console.log(res)
      } catch (error) {
        console.error(error)
      }
    }
  }
}

2. 在 Vuex 中使用

js 复制代码
import request from '@/utils/request'

export default {
  actions: {
    async login({ commit }, payload) {
      const res = await request.post('/api/login', payload)
      commit('SET_TOKEN', res.token)
    }
  }
}

三、支持 TypeScript(可选)

如果你使用的是 Vue + TypeScript(如 Vite + Vue 3 + TS),可以添加类型定义:

1. 定义统一返回结构

ts 复制代码
// src/types/index.ts
export interface ApiResponse<T = any> {
  code: number
  message: string
  data: T
}

2. 使用泛型调用

ts 复制代码
import { ApiResponse } from '@/types'

interface User {
  id: number
  name: string
}

const res = await request.get<ApiResponse<User>>('/api/user/1')
console.log(res.data.name)

四、取消重复请求(防抖)

建议常用事件无论是否发生请求也做防抖,避免重复性耗费资源

防止用户多次点击按钮导致重复请求:

js 复制代码
import axios from 'axios'

const pendingMap = new Map()

function generateReqKey(config) {
  return [config.method, config.url].join('&')
}

function addPending(config) {
  const key = generateReqKey(config)
  const controller = new AbortController()
  config.signal = controller.signal
  if (!pendingMap.has(key)) {
    pendingMap.set(key, controller)
  }
}

function removePending(key) {
  if (pendingMap.has(key)) {
    const controller = pendingMap.get(key)
    controller.abort()
    pendingMap.delete(key)
  }
}

// 请求拦截器
service.interceptors.request.use(config => {
  addPending(config)
  return config
})

// 响应拦截器
service.interceptors.response.use(response => {
  removePending(generateReqKey(response.config))
  return response
}, error => {
  removePending(generateReqKey(error.config))
  return Promise.reject(error)
})

五、全局 loading(可选)

可以在请求拦截器中添加 loading,在响应拦截器中关闭:

js 复制代码
let loadingCount = 0

function startLoading() {
  if (loadingCount === 0) {
    // 显示 loading 动画
    store.dispatch('showLoading')
  }
  loadingCount++
}

function endLoading() {
  loadingCount--
  if (loadingCount <= 0) {
    store.dispatch('hideLoading')
  }
}

// 请求拦截器
service.interceptors.request.use(config => {
  startLoading()
  return config
})

// 响应拦截器
service.interceptors.response.use(response => {
  endLoading()
  return response
}, error => {
  endLoading()
  return Promise.reject(error)
})

六、推荐目录结构

复制代码
src/
├── utils/
│   └── request.js       # axios 封装
├── types/
│   └── index.ts          # 接口类型定义(TS)
├── api/
│   ├── user.js           # 用户相关接口
│   ├── product.js        # 商品相关接口
│   └── index.js          # 导出所有 API
├── views/
│   └── ...               # 页面组件
└── store/
    └── index.js          # Vuex 状态管理(可选)

七、API 模块化示例(src/api/user.js

js 复制代码
import request from '@/utils/request'

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

export function getUserInfo(token) {
  return request({
    url: '/user/info',
    method: 'get',
    params: { token }
  })
}

在组件中使用:

js 复制代码
import { login } from '@/api/user'

export default {
  methods: {
    async handleLogin() {
      const res = await login(this.loginForm)
      console.log(res)
    }
  }
}

总结:要点

特性 实现方式
请求拦截 添加 token、loading
响应拦截 统一处理成功/失败逻辑
错误提示 使用 UI 框架提示(如 element-ui、vant)
类型安全 TypeScript 泛型支持
取消重复请求 使用 AbortController
模块化组织 按功能拆分 API 文件
全局 loading 请求计数器控制显示/隐藏
相关推荐
UIUV20 小时前
JavaScript 学习笔记:深入理解 map() 方法与面向对象特性
前端·javascript·代码规范
玉宇夕落20 小时前
深入剖析 JavaScript 中 map() 与 parseInt 的“经典组合陷阱”
javascript·面试
太平洋月光21 小时前
MJML邮件如何随宽度变化动态切换有几列📮
前端·css
AAA不会前端开发21 小时前
TypeScript核心类型系统完全指南
前端·typescript
徐同保21 小时前
使用GitKraken把feature_xtb_1104分支的多次提交记录合并到一起,只保留一次提交记录,并合并到master分支
前端
小光学长21 小时前
基于Vue的智慧楼宇报修平台设计与实现066z15wb(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
醉方休21 小时前
web前端 DSL转换技术
前端
sen_shan21 小时前
Vue3+Vite+TypeScript+Element Plus开发-27.表格页码自定义
前端·javascript·typescript
刺客_Andy21 小时前
React 第五十二节 Router中 useResolvedPath使用详解和注意事项示例
前端·react.js·架构
豆浆94521 小时前
vue3+qiankun主应用和微应用的路由跳转返回
前端