Vue3 + axios 前后端联调实战:封装、跨域与报错处理

在 Vue3 项目开发中,前后端联调是核心环节之一,而 axios 作为最主流的 HTTP 客户端库,承担着前端与后端数据交互的重任。

很多新手在使用 axios 时,会遇到三个高频痛点:

  1. 每个请求都重复写基础配置(如 baseURL、请求头),代码冗余;
  2. 本地开发时浏览器跨域报错,无法访问后端接口;
  3. 接口报错、网络异常、登录失效时,没有统一的处理逻辑,用户体验差。

一、环境准备

首先确保你的项目是 Vue3 + Vite 版本(Vue3 CLI 项目仅跨域配置略有差异,核心逻辑通用)。

1.1 安装 axios

打开项目终端,执行安装命令(三选一即可):

bash 复制代码
npm install axios
# 或 yarn add axios
# 或 pnpm add axios

二、核心:axios 全局封装(重点)

封装的核心目标:统一配置、统一拦截、统一处理,避免重复代码,提升维护性,后续所有接口都复用该封装实例。

2.1 创建封装文件

在项目 src 目录下新建 utils 文件夹,再创建 request.js 文件(固定路径:src/utils/request.js),专门用于封装 axios。

2.2 完整封装代码(可直接复制使用)

代码带详细注释,关键步骤标注,新手可快速理解:

javascript 复制代码
// 1. 引入 axios(核心依赖)
import axios from 'axios'
// 可选:引入 Element Plus 消息提示(项目常用,无侵权,可替换为其他UI库)
import { ElMessage } from 'element-plus'

// 2. 创建 axios 实例(核心步骤)
const service = axios.create({
  // 基础路径:后续请求会自动拼接在前面(用环境变量配置,灵活切换环境)
  baseURL: import.meta.env.VITE_API_URL,
  // 请求超时时间:10秒(避免请求一直挂起)
  timeout: 10000,
  // 默认请求头(适配 JSON 格式接口,无需手动添加)
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  }
})

// 3. 请求拦截器:发送请求前统一处理(比如携带Token)
service.interceptors.request.use(
  (config) => {
    // 场景1:自动携带 Token(登录后存储在 localStorage,按需修改存储方式)
    const token = localStorage.getItem('token')
    if (token) {
      // 格式需和后端一致,一般是 Bearer + 空格 + Token
      config.headers.Authorization = `Bearer ${token}`
    }
    // 场景2:可在这里添加请求加载动画(比如 Element Plus 的 ElLoading)
    // ElLoading.service({ text: '加载中...' })
    return config
  },
  (error) => {
    // 请求发送失败处理(比如网络中断、请求参数错误)
    console.error('请求拦截错误:', error)
    return Promise.reject(error)
  }
)

// 4. 响应拦截器:接口返回数据后统一处理(报错、数据过滤)
service.interceptors.response.use(
  (response) => {
    // 直接返回后端数据(简化取值,无需每次都写 response.data)
    // 可根据后端返回格式调整,比如后端统一返回 { code: 200, data: {}, msg: '' },可在这里判断 code
    // if (response.data.code !== 200) { ElMessage.error(response.data.msg); return Promise.reject() }
    return response.data
  },
  (error) => {
    // 统一报错处理:根据 HTTP 状态码判断异常类型(重点!)
    const { response } = error
    if (response) {
      // 有响应:接口返回错误(HTTP状态码非200)
      switch (response.status) {
        // 401:未授权/登录失效(最常见场景)
        case 401:
          ElMessage.error('登录已失效,请重新登录')
          // 清空本地存储(Token、用户信息等)
          localStorage.clear()
          // 路由跳转至登录页(需先引入路由实例,路径按需修改)
          // import router from '../router'
          // router.push('/login')
          break
        // 403:无权限(比如普通用户访问管理员接口)
        case 403:
          ElMessage.error('没有权限访问该接口')
          break
        // 404:接口不存在(路径写错、后端接口未部署)
        case 404:
          ElMessage.error('请求接口不存在,请检查接口路径')
          break
        // 500:服务器错误(后端代码报错、服务器宕机)
        case 500:
          ElMessage.error('服务器异常,请稍后重试')
          break
        // 其他错误(比如 400 参数错误)
        default:
          ElMessage.error(response.data?.msg || '请求失败,请稍后再试')
      }
    } else {
      // 无响应:网络异常、服务器未启动、跨域配置错误
      ElMessage.error('网络异常,请检查网络连接或服务器状态')
    }
    // 抛出错误,便于页面中额外处理(可选)
    return Promise.reject(error)
  }
)

// 5. 导出 axios 实例,供其他文件引入使用
export default service

2.3 封装核心说明(必看)

  • baseURL:使用 Vite 环境变量配置,区分开发/生产环境,无需手动修改代码;
  • 请求拦截器:自动携带 Token,无需每个请求手动添加,还可添加加载动画;
  • 响应拦截器:统一处理 HTTP 状态码报错,无需每个接口单独写 try/catch;
  • 错误提示:集成 Element Plus 消息提示,若未使用 Element Plus,可替换为原生 alert。

三、接口统一管理(推荐)

不建议在页面中直接写接口地址和请求逻辑,单独管理接口,便于后续维护(比如接口路径修改,只需改一处)。

3.1 创建接口文件

src 目录下新建 api 文件夹,按业务模块划分接口文件(比如用户相关、商品相关),这里以用户接口为例,创建 user.js(路径:src/api/user.js)。

3.2 接口代码示例(可直接复制)

javascript 复制代码
// 引入封装好的 axios 实例(路径按需调整)
import request from '../utils/request'

// 1. 登录接口(POST 请求,参数为 JSON 格式)
export function login(data) {
  return request({
    url: '/user/login', // 接口路径(会拼接 baseURL)
    method: 'post',     // 请求方式
    data                // 请求参数(POST 用 data,GET 用 params)
  })
}

// 2. 获取用户信息(GET 请求,参数为 URL 拼接)
export function getUserInfo(params) {
  return request({
    url: '/user/info',
    method: 'get',
    params // 比如 { id: 123 },会自动拼接为 /user/info?id=123
  })
}

// 3. 上传文件接口(POST + FormData 格式,特殊场景)
export function uploadFile(formData) {
  return request({
    url: '/file/upload',
    method: 'post',
    // 上传文件需手动设置 Content-Type,覆盖默认的 JSON 格式
    headers: { 'Content-Type': 'multipart/form-data' },
    data: formData // FormData 实例,比如 new FormData()
  })
}

四、页面中使用接口(实战演示)

在 Vue3 组件(Setup 语法)中,直接引入接口函数调用,简洁高效,结合 async/await 处理异步请求。

vue 复制代码
<template>
  <div class="login-page">
    <button @click="handleLogin" class="login-btn">登录</button>
  </div>
</template>

<script setup>
// 1. 引入需要的接口函数(路径按需调整)
import { login, getUserInfo } from '../api/user'
// 可选:引入 Element Plus 消息提示(优化用户体验)
import { ElMessage } from 'element-plus'

// 2. 登录按钮点击事件(异步函数)
const handleLogin = async () => {
  try {
    // 调用登录接口,传递参数(和后端接口参数一致)
    const res = await login({ username: 'admin', password: '123456' })
    console.log('登录成功:', res)
    // 登录成功后,存储 Token 到 localStorage
    localStorage.setItem('token', res.token)
    // 调用获取用户信息接口(登录后才能访问,需携带 Token)
    const userInfo = await getUserInfo()
    console.log('用户信息:', userInfo)
    // 登录成功提示
    ElMessage.success('登录成功!')
  } catch (error) {
    // 拦截器已统一报错,这里可处理额外逻辑(比如关闭加载动画)
    console.log('请求失败:', error)
  }
}
</script>

<style scoped>
.login-btn {
  padding: 8px 16px;
  cursor: pointer;
}
</style>

五、解决前端跨域问题(高频痛点)

5.1 什么是跨域?

浏览器同源策略限制:协议、域名、端口 任意一个不同,就会触发跨域,导致接口请求失败(控制台报错:Access to XMLHttpRequest at ... from origin ... has been blocked by CORS policy)。

示例:前端(http://localhost:3000)访问后端(http://localhost:8080),端口不同,触发跨域。

5.2 Vite 项目配置跨域(开发环境,重点)

开发环境下,通过 Vite 代理解决跨域,修改项目根目录的 vite.config.js 文件,添加 proxy 配置:

javascript 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()], // Vite 基础插件(Vue3 必选)
  // 跨域代理配置(核心)
  server: {
    port: 3000, // 前端启动端口(可自行修改)
    proxy: {
      // 匹配以 /api 开头的所有请求(和环境变量 VITE_API_URL 对应)
      '/api': {
        target: 'http://localhost:8080', // 后端真实接口地址(替换为你的后端地址)
        changeOrigin: true, // 允许跨域(关键配置)
        rewrite: (path) => path.replace(/^\/api/, '') // 路径重写:去掉 /api 前缀(按需调整)
      }
    }
  }
})

5.3 环境变量配置(推荐,灵活切换环境)

在项目根目录新建 2 个环境变量文件,区分开发/生产环境,无需手动修改代码。

  1. 新建 .env.development(开发环境,本地开发用):
plain 复制代码
# 开发环境接口基础路径(和 vite.config.js 中的 proxy 匹配)
VITE_API_URL = '/api'
  1. 新建 .env.production(生产环境,上线部署用):
plain 复制代码
# 生产环境真实接口地址(替换为你的线上接口地址)
VITE_API_URL = 'http://api.xxx.com'

说明:开发环境走代理(/api 代理到后端地址),生产环境直接访问真实接口,无需修改封装代码。

5.4 Vue CLI 项目跨域配置(补充)

如果你的项目是 Vue3 CLI 搭建的(不是 Vite),修改项目根目录的 vue.config.js 文件:

javascript 复制代码
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080', // 后端真实接口地址
        changeOrigin: true, // 允许跨域
        pathRewrite: { '^/api': '' } // 路径重写(按需调整)
      }
    }
  }
}

六、完整报错处理方案

封装后的 axios 已经实现了统一报错,覆盖所有常见异常场景,无需在每个页面单独处理错误,大幅减少代码冗余:

  1. 网络异常:无响应(服务器未启动、网络中断),提示「网络异常,请检查网络或服务器状态」;
  2. 登录失效(401):自动清空 Token,提示「登录已失效,请重新登录」,并跳转到登录页;
  3. 无权限(403):提示「没有权限访问该接口」;
  4. 接口不存在(404):提示「请求接口不存在,请检查接口路径」;
  5. 服务器错误(500):提示「服务器异常,请稍后重试」;
  6. 其他业务报错:直接显示后端返回的错误信息(response.data.msg)。

七、注意事项(避坑指南)

  • Token 存储:示例用 localStorage,若项目需要更安全的存储方式,可替换为 pinia/vuex 状态管理(刷新页面不丢失);
  • 路由跳转:401 跳转登录页时,需先引入路由实例(import router from '.../router'),否则会报错;
  • 跨域仅开发环境有效:生产环境跨域需后端配置 CORS(设置 Access-Control-Allow-Origin),前端代理配置不生效;
  • Content-Type 注意 :上传文件需手动设置 multipart/form-data,普通 JSON 接口无需修改;
  • 环境变量命名 :Vite 环境变量必须以 VITE_ 开头,否则无法读取(比如 VITE_API_URL,不能写成 API_URL);
  • 代码复用:所有接口都复用封装的 request 实例,不要直接使用 axios 发起请求,否则无法触发拦截器。

八、总结

本文完整实现了 Vue3 + axios 前后端联调的核心实战内容,覆盖新手必学的三个核心知识点:

  1. axios 封装:统一配置、拦截器、报错处理,减少重复代码,提升项目维护性;
  2. 接口管理:模块化管理接口,按业务划分,后续修改接口路径更便捷;
  3. 跨域解决:适配 Vite/Vue CLI 项目,一键配置代理,解决本地开发跨域痛点;
  4. 报错处理:覆盖所有常见异常场景,统一提示,优化用户体验。
相关推荐
研來如此3 小时前
C++ 接口设计 && Doxygen 注释
前端·javascript·c++
野槐4 小时前
Electron开发
前端·javascript·electron
#做一个清醒的人4 小时前
【Electron】开发两年Electron项目评估报告
前端·electron
lizhongxuan10 小时前
Claude Code 防上下文爆炸:源码级深度解析
前端·后端
柳杉11 小时前
震惊!字符串还能这么玩!
前端·javascript
是上好佳佳佳呀12 小时前
【前端(五)】CSS 知识梳理:浮动与定位
前端·css
wefly201712 小时前
纯前端架构深度解析:jsontop.cn,JSON 格式化与全栈开发效率平台
java·前端·python·架构·正则表达式·json·php
我命由我1234514 小时前
React - 类组件 setState 的 2 种写法、LazyLoad、useState
前端·javascript·react.js·html·ecmascript·html5·js
自由生长202414 小时前
IndexedDB的观察
前端