企业级Axios封装实战指南

以下是一套企业级 Axios 封装方案,包含请求 / 响应拦截、统一错误处理、请求取消、环境区分、请求重试等核心功能,可直接集成到 Vue/React/ 纯前端项目中:

一、封装目录结构

复制代码
src/
├── utils/
│   ├── request.js       # Axios 核心封装
│   └── cancelToken.js   # 请求取消工具(可选)
├── config/
│   └── env.js           # 环境配置(区分开发/测试/生产)
└── api/                 # 接口管理(按业务拆分)
    ├── user.js          # 用户相关接口
    └── order.js         # 订单相关接口

二、第一步:环境配置(config/env.js)

区分不同环境的接口基础地址,避免硬编码:

javascript 复制代码
// 环境判断(基于 Vite/Webpack 环境变量)
const env = import.meta.env.MODE || 'development';

// 环境配置
const envConfig = {
  development: {
    baseURL: 'http://10.230.2.24:8083', // 开发环境
  },
  test: {
    baseURL: 'http://1.95.205.197:8083', // 测试环境
  },
  production: {
    baseURL: 'https://magicpower.sh-liangxin.com/cloud-api', // 生产环境
  },
};

export default {
  env,
  ...envConfig[env],
};

三、第二步:Axios 核心封装(utils/request.js)

javascript 复制代码
import axios from 'axios';
import envConfig from '@/config/env';
import { ElMessage, ElMessageBox } from 'element-plus'; // 按需替换为你的UI库
import { getToken, clearToken, redirectLogin } from '@/utils/auth'; // 自定义token工具

// 创建Axios实例
const service = axios.create({
  baseURL: envConfig.baseURL, // 基础地址
  timeout: 10000, // 超时时间(10秒)
  headers: {
    'Content-Type': 'application/json;charset=utf-8',
  },
});

// ===================== 请求拦截器 =====================
service.interceptors.request.use(
  (config) => {
    // 1. 添加token(登录态)
    const token = getToken(); // 从localStorage/cookie获取token
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }

    // 2. GET请求参数序列化(可选)
    if (config.method === 'get' && config.params) {
      // 解决GET请求参数中数组/对象的序列化问题
      config.paramsSerializer = {
        serialize: (params) => {
          return new URLSearchParams(params).toString();
        },
      };
    }

    // 3. 自定义请求头(如接口版本号)
    config.headers['X-API-Version'] = 'v1';

    return config;
  },
  (error) => {
    // 请求前的错误(如参数错误)
    console.error('请求拦截器错误:', error);
    return Promise.reject(error);
  }
);

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

    // 1. 接口自定义状态码处理(根据后端约定调整)
    const code = res.code || res.status;
    // 成功状态码(示例:200/0表示成功)
    if (code === 200 || code === 0) {
      return res;
    }

    // 2. 业务错误处理(如参数错误、权限不足)
    ElMessage.error(res.msg || '接口请求失败');
    return Promise.reject(res);
  },
  (error) => {
    // 网络/HTTP错误处理
    const { response, message } = error;

    // 1. 取消请求的错误(不提示)
    if (axios.isCancel(error)) {
      console.log('请求已取消:', message);
      return Promise.reject(error);
    }

    // 2. 超时错误
    if (message.includes('timeout')) {
      ElMessage.error('请求超时,请稍后重试');
      return Promise.reject(error);
    }

    // 3. 无响应(网络错误)
    if (!response) {
      ElMessage.error('网络异常,请检查网络连接');
      return Promise.reject(error);
    }

    // 4. HTTP状态码处理
    const status = response.status;
    switch (status) {
      case 401: // 未授权/Token过期
        ElMessageBox.confirm(
          '登录状态已过期,请重新登录',
          '提示',
          {
            confirmButtonText: '重新登录',
            cancelButtonText: '取消',
            type: 'warning',
          }
        ).then(() => {
          clearToken(); // 清除token
          redirectLogin(); // 跳登录页
        });
        break;
      case 403: // 权限拒绝
        ElMessage.error('暂无权限访问该资源');
        break;
      case 404: // 接口不存在
        ElMessage.error('请求地址不存在');
        break;
      case 500: // 服务器错误
        ElMessage.error('服务器内部错误,请稍后重试');
        break;
      default:
        ElMessage.error(`请求失败:${status} - ${response.data?.msg || '未知错误'}`);
    }

    return Promise.reject(error);
  }
);

// ===================== 请求重试(可选) =====================
// 适用于网络抖动导致的偶发失败,需安装 axios-retry
// import axiosRetry from 'axios-retry';
// axiosRetry(service, {
//   retries: 3, // 重试次数
//   retryDelay: (retryCount) => {
//     return retryCount * 1000; // 每次重试间隔1秒
//   },
//   retryCondition: (error) => {
//     // 仅对5xx错误/网络错误重试
//     return error.response?.status >= 500 || !error.response;
//   },
// });

// ===================== 导出核心方法 =====================
// 封装常用请求方法,简化调用
const request = {
  get: (url, params = {}, config = {}) => {
    return service.get(url, { params, ...config });
  },
  post: (url, data = {}, config = {}) => {
    return service.post(url, data, config);
  },
  put: (url, data = {}, config = {}) => {
    return service.put(url, data, config);
  },
  delete: (url, params = {}, config = {}) => {
    return service.delete(url, { params, ...config });
  },
  // 上传文件(FormData)
  upload: (url, formData, config = {}) => {
    return service.post(url, formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
      ...config,
    });
  },
};

// 导出Axios实例(用于特殊场景)和封装方法
export default request;
export { service };

四、第三步:请求取消工具(utils/cancelToken.js)

用于防抖场景(如搜索框、快速点击),避免重复请求:

javascript 复制代码
import axios from 'axios';

// 存储取消请求的函数
const cancelRequestMap = new Map();

/**
 * 取消重复请求
 * @param {string} url - 请求地址
 * @param {string} msg - 取消提示
 */
export const cancelRepeatRequest = (url, msg = '重复请求已取消') => {
  if (cancelRequestMap.has(url)) {
    const cancel = cancelRequestMap.get(url);
    cancel(msg);
    cancelRequestMap.delete(url);
  }
};

/**
 * 获取取消令牌配置
 * @param {string} url - 请求地址
 * @returns {object} 取消令牌配置
 */
export const getCancelTokenConfig = (url) => {
  // 先取消已存在的请求
  cancelRepeatRequest(url);
  return {
    cancelToken: new axios.CancelToken((cancel) => {
      cancelRequestMap.set(url, cancel);
    }),
  };
};

/**
 * 清空所有取消请求
 */
export const clearAllCancelRequest = () => {
  for (const cancel of cancelRequestMap.values()) {
    cancel('页面销毁,取消所有请求');
  }
  cancelRequestMap.clear();
};

五、第四步:接口管理(api/user.js)

按业务模块拆分接口,统一管理,便于维护:

javascript 复制代码
import request from '@/utils/request';
import { getCancelTokenConfig } from '@/utils/cancelToken';

// 用户登录
export const login = (data) => {
  return request.post('/user/login', data);
};

// 获取用户信息
export const getUserInfo = () => {
  return request.get('/user/info');
};

// 搜索用户(带取消请求,防抖)
export const searchUser = (keyword) => {
  const url = '/user/search';
  return request.get(url, { keyword }, getCancelTokenConfig(url));
};

// 上传用户头像
export const uploadAvatar = (formData) => {
  return request.upload('/user/avatar', formData);
};

// 修改用户信息
export const updateUser = (id, data) => {
  return request.put(`/user/${id}`, data);
};

// 删除用户
export const deleteUser = (id) => {
  return request.delete('/user', { id });
};

六、使用示例(Vue 组件中)

javascript 复制代码
<script setup lang="ts">
import { ref, onUnmounted } from 'vue';
import { login, searchUser } from '@/api/user';
import { clearAllCancelRequest } from '@/utils/cancelToken';

const loading = ref(false);

// 登录示例
const handleLogin = async () => {
  loading.value = true;
  try {
    const res = await login({
      username: 'admin',
      password: '123456',
    });
    console.log('登录成功:', res);
    // 存储token、跳首页等逻辑
  } catch (err) {
    console.error('登录失败:', err);
  } finally {
    loading.value = false;
  }
};

// 搜索用户(防抖)
const handleSearch = async (keyword) => {
  try {
    const res = await searchUser(keyword);
    console.log('搜索结果:', res);
  } catch (err) {
    if (!axios.isCancel(err)) {
      console.error('搜索失败:', err);
    }
  }
};

// 页面销毁时清空所有取消请求
onUnmounted(() => {
  clearAllCancelRequest();
});
</script>

<template>
  <el-button @click="handleLogin" :loading="loading">登录</el-button>
  <el-input
    placeholder="输入用户名搜索"
    @input="handleSearch"
    clearable
  />
</template>

七、核心特性说明

特性 说明
环境区分 自动适配开发 / 测试 / 生产环境的接口地址,无需手动修改
请求拦截 统一添加 token、自定义请求头,处理 GET 参数序列化
响应拦截 统一处理业务状态码、HTTP 状态码,全局错误提示
错误分类处理 区分取消请求、超时、网络错误、401/403/500 等场景,针对性提示
请求取消 解决重复请求问题(如搜索框防抖、快速点击)
方法封装 简化 get/post/put/delete/upload 调用,参数更清晰
可扩展 支持请求重试、FormData 上传、自定义配置等高级功能

八、适配调整建议

  1. UI 库替换 :若不用 Element Plus,将ElMessage/ElMessageBox替换为你的 UI 库(如 Ant Design Vue 的message/modal);
  2. 状态码调整 :根据后端实际约定的成功 / 失败状态码修改响应拦截器中的code判断逻辑;
  3. Token 存储getToken/clearToken需实现自己的 token 存储逻辑(如 localStorage/cookie);
  4. 请求重试 :如需开启,安装axios-retry并取消注释对应代码;
  5. TypeScript 适配:可给 request 方法添加 TS 类型定义,提升类型提示。

这套封装兼顾了易用性和健壮性,符合企业级项目的开发规范,可直接复用或根据业务需求微调。

相关推荐
一 乐2 小时前
景区管理|基于springboot + vue景区管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习
幽络源小助理2 小时前
SpringBoot+Vue大型商场应急预案管理系统源码 | Java安全类项目免费下载 – 幽络源
java·vue.js·spring boot
JIngJaneIL2 小时前
基于java + vue连锁门店管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
毕设十刻3 小时前
基于Vue的养老服务平台85123(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
释怀不想释怀3 小时前
vue前端crud(页面布局,新增,vue中反向代理)
前端·javascript·vue.js
麦麦大数据3 小时前
F068 vue+flask 非遗文化遗产图谱可视化系统
前端·vue.js·flask·知识图谱·文化遗产·非遗文化
QiHY3 小时前
通过Spring Authorization Server对vue应用进行授权防护
java·vue.js·spring·oauth
老华带你飞3 小时前
电影购票|基于java+ vue电影购票系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
老华带你飞3 小时前
宠物管理|基于java+ vue宠物管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·宠物