axios封装对比

现axios封装

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

// 当前环境语言选项
const language = window.localStorage
  .getItem('language-option')
  ?.replace(/"/g, '');

// 向全局添加下载进度
const addDownloadProgress = (progressEvent) => {
  if (progressEvent.lengthComputable) {
    window.isFileDownloading = progressEvent.loaded !== progressEvent.total;
  } else {
    window.isFileDownloading = false;
  }
};

axios.defaults.timeout = 100000;
axios.defaults.withCredentials = true;

const axiosInstance = axios.create({
  baseURL: '',
});

axiosInstance.interceptors.request.use(
  (config) => {
    config.headers.Feign = 'feign';
    const csrftoken = localStorage.getItem('csrftoken');
    if (csrftoken) {
      config.headers.forgerydefense = csrftoken.replace(/"/g, '');
    }
    config.headers['language-option'] = language || 'zh-CN';
    config.headers['logName'] = localStorage.getItem('userid');
    if (config.method === 'get' && config.params) {
      const tmpParams = {};
      for (const key in config.params) {
        if (config.params[key] === undefined) continue;
        if (config.params[key] instanceof Array) {
          tmpParams[key] = config.params[key];
        } else {
          tmpParams[key] = encodeURIComponent(config.params[key]);
        }
      }
      config.params = tmpParams;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

axiosInstance.interceptors.response.use(
  (response) => (response.status === 200 ? response : response),
  (error) => {
    const { response } = error;
    return response ? Promise.reject(response.data) : Promise.reject(error);
  }
);

const request = (config) => {
  const conf = config;
  if (conf.responseType === 'blob') {
    if (!conf['onDownloadProgress']) {
      conf['onDownloadProgress'] = addDownloadProgress;
    }
  }
  return new Promise((resolve) => {
    axiosInstance.request(conf).then((res) => {
      resolve(res.data instanceof Blob ? res : res.data);
    });
  });
};

export function get(config) {
  return request({ ...config, method: 'GET' });
}

export function post(config) {
  return request({ ...config, method: 'POST' });
}

export default request;

Axios封装分析报告

  1. 优点分析
    基础配置合理
javascript 复制代码
axios.defaults.timeout = 100000;        // 100秒超时,适合大文件下载
axios.defaults.withCredentials = true;  // 支持跨域携带cookie

请求拦截器功能完善

javascript 复制代码
axiosInstance.interceptors.request.use((config) => {
  // 添加Feign标识
  config.headers.Feign = 'feign';
  
  // CSRF防护
  const csrftoken = localStorage.getItem('csrftoken');
  if (csrftoken) {
    config.headers.forgerydefense = csrftoken.replace(/"/g, '');
  }
  
  // 国际化支持
  config.headers['language-option'] = language || 'zh-CN';
  
  // 用户标识
  config.headers['logName'] = localStorage.getItem('userid');
  
  // GET参数编码处理
  if (config.method === 'get' && config.params) {
    // 处理undefined值和数组
  }
  
  return config;
});

下载进度支持

javascript 复制代码
const addDownloadProgress = (progressEvent) => {
  if (progressEvent.lengthComputable) {
    window.isFileDownloading = progressEvent.loaded !== progressEvent.total;
  }
};
  1. 存在的问题
    严重问题
  2. 响应拦截器过于简单
javascript 复制代码
axiosInstance.interceptors.response.use(
  (response) => (response.status === 200 ? response : response), // 逻辑冗余
  (error) => {
    const { response } = error;
    return response ? Promise.reject(response.data) : Promise.reject(error);
  }
);

问题:

没有统一的错误处理

没有统一的成功响应格式处理

没有网络错误、超时等特殊情况的处理

  1. 请求函数设计不合理
javascript 复制代码
const request = (config) => {
  // ...
  return new Promise((resolve) => {  // 只resolve,不reject
    axiosInstance.request(conf).then((res) => {
      resolve(res.data instanceof Blob ? res : res.data);
    });
  });
};

问题:

使用Promise包装axios,但只处理resolve,不处理reject

错误处理不完整

可能导致Promise永远pending

  1. 参数编码逻辑有缺陷
javascript 复制代码
if (config.method === 'get' && config.params) {
  const tmpParams = {};
  for (const key in config.params) {
    if (config.params[key] === undefined) continue;
    if (config.params[key] instanceof Array) {
      tmpParams[key] = config.params[key];
    } else {
      tmpParams[key] = encodeURIComponent(config.params[key]); // 可能导致双重编码
    }
  }
  config.params = tmpParams;
}

问题:

可能导致双重URL编码

数组处理不完整

没有处理null值

  1. 改进建议

  2. 完善响应拦截器

javascript 复制代码
axiosInstance.interceptors.response.use(
  (response) => {
    // 统一处理成功响应
    if (response.status === 200) {
      return response.data;
    }
    return Promise.reject(new Error(`HTTP ${response.status}`));
  },
  (error) => {
    // 统一错误处理
    if (error.response) {
      // 服务器返回错误状态码
      const { status, data } = error.response;
      switch (status) {
        case 401:
          // 处理未授权
          break;
        case 403:
          // 处理禁止访问
          break;
        case 404:
          // 处理资源不存在
          break;
        case 500:
          // 处理服务器错误
          break;
      }
      return Promise.reject(data);
    } else if (error.request) {
      // 网络错误
      return Promise.reject(new Error('网络连接失败'));
    } else {
      // 其他错误
      return Promise.reject(error);
    }
  }
);
  1. 重构请求函数
javascript 复制代码
const request = async (config) => {
  const conf = config;
  
  // 处理下载进度
  if (conf.responseType === 'blob') {
    if (!conf['onDownloadProgress']) {
      conf['onDownloadProgress'] = addDownloadProgress;
    }
  }
  
  try {
    const response = await axiosInstance.request(conf);
    return response.data instanceof Blob ? response : response.data;
  } catch (error) {
    // 统一错误处理
    throw error;
  }
};
  1. 优化参数处理
javascript 复制代码
const request = async (config) => {
  const conf = config;
  
  // 处理下载进度
  if (conf.responseType === 'blob') {
    if (!conf['onDownloadProgress']) {
      conf['onDownloadProgress'] = addDownloadProgress;
    }
  }
  
  try {
    const response = await axiosInstance.request(conf);
    return response.data instanceof Blob ? response : response.data;
  } catch (error) {
    // 统一错误处理
    throw error;
  }
};
  1. 添加请求重试机制
javascript 复制代码
const requestWithRetry = async (config, retries = 3) => {
  try {
    return await request(config);
  } catch (error) {
    if (retries > 0 && shouldRetry(error)) {
      await delay(1000);
      return requestWithRetry(config, retries - 1);
    }
    throw error;
  }
};
  1. 总体评价
    评分:6/10
    优点:
    基础配置合理
    请求拦截器功能较完善
    支持下载进度
    有CSRF防护和国际化支持
    缺点:
    响应拦截器过于简单
    错误处理不完整
    请求函数设计有缺陷
    参数处理逻辑有问题
    缺少重试机制
    缺少请求/响应日志
    建议:
    完善错误处理机制
    重构请求函数
    优化参数处理逻辑
    添加请求重试功能
    增加请求/响应日志
    添加请求取消功能
    统一响应格式处理
    这个axios封装在基础功能上做得不错,但在错误处理和用户体验方面还有很大改进空间。

加强版本

javascript 复制代码
/**
 * Axios HTTP请求封装
 * 提供统一的请求处理、错误处理、重试机制、缓存等功能
 *
 *
 * @version 2.0.0
 * @description 增强版HTTP请求封装,支持请求重试、缓存、取消、进度监控等功能
 */

import axios from 'axios';

// ==================== 配置常量 ====================
const CONFIG = {
  // 请求配置
  TIMEOUT: 100000, // 默认超时时间(ms)
  RETRY_TIMES: 3, // 默认重试次数
  RETRY_DELAY: 1000, // 重试延迟时间(ms)
  CACHE_EXPIRE: 5 * 60 * 1000, // 缓存过期时间(ms)

  // 状态码
  SUCCESS_CODE: 200, // 成功状态码
  UNAUTHORIZED: 401, // 未授权
  FORBIDDEN: 403, // 禁止访问
  NOT_FOUND: 404, // 资源不存在
  SERVER_ERROR: 500, // 服务器错误

  // 请求头
  HEADERS: {
    'Content-Type': 'application/json',
    Feign: 'feign',
  },
};

// ==================== 工具函数 ====================

/**
 * 延迟函数
 * @param {number} ms - 延迟毫秒数
 * @returns {Promise} 延迟Promise
 */
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

/**
 * 生成缓存键
 * @param {Object} config - 请求配置
 * @returns {string} 缓存键
 */
const generateCacheKey = (config) => {
  const { url, method, params, data } = config;
  return `${method}:${url}:${JSON.stringify(params)}:${JSON.stringify(data)}`;
};

/**
 * 判断是否应该重试请求
 * @param {Error} error - 错误对象
 * @returns {boolean} 是否应该重试
 */
const shouldRetry = (error) => {
  // 网络错误或超时错误应该重试
  if (!error.response) return true;

  // 服务器错误(5xx)应该重试
  const status = error.response.status;
  return status >= 500 && status < 600;
};

/**
 * 处理请求参数
 * @param {Object} params - 原始参数
 * @returns {Object} 处理后的参数
 */
const processParams = (params) => {
  if (!params || typeof params !== 'object') return params;

  const processed = {};
  for (const [key, value] of Object.entries(params)) {
    // 跳过undefined和null值
    if (value === undefined || value === null) continue;

    if (Array.isArray(value)) {
      processed[key] = value;
    } else if (typeof value === 'object') {
      // 对象类型转为JSON字符串
      processed[key] = JSON.stringify(value);
    } else {
      // 其他类型转为字符串
      processed[key] = String(value);
    }
  }
  return processed;
};

// ==================== 缓存管理 ====================

/**
 * 简单的内存缓存实现
 */
class RequestCache {
  constructor() {
    this.cache = new Map();
  }

  /**
   * 设置缓存
   * @param {string} key - 缓存键
   * @param {any} value - 缓存值
   * @param {number} expire - 过期时间(ms)
   */
  set(key, value, expire = CONFIG.CACHE_EXPIRE) {
    this.cache.set(key, {
      value,
      expire: Date.now() + expire,
    });
  }

  /**
   * 获取缓存
   * @param {string} key - 缓存键
   * @returns {any} 缓存值或null
   */
  get(key) {
    const item = this.cache.get(key);
    if (!item) return null;

    if (Date.now() > item.expire) {
      this.cache.delete(key);
      return null;
    }

    return item.value;
  }

  /**
   * 清除缓存
   * @param {string} key - 缓存键(可选,不传则清除所有)
   */
  clear(key) {
    if (key) {
      this.cache.delete(key);
    } else {
      this.cache.clear();
    }
  }
}

const requestCache = new RequestCache();

// ==================== 请求取消管理 ====================

/**
 * 请求取消管理器
 */
class CancelManager {
  constructor() {
    this.pendingRequests = new Map();
  }

  /**
   * 添加请求到待处理列表
   * @param {string} key - 请求标识
   * @param {Function} cancel - 取消函数
   */
  add(key, cancel) {
    this.pendingRequests.set(key, cancel);
  }

  /**
   * 移除请求
   * @param {string} key - 请求标识
   */
  remove(key) {
    this.pendingRequests.delete(key);
  }

  /**
   * 取消指定请求
   * @param {string} key - 请求标识
   */
  cancel(key) {
    const cancel = this.pendingRequests.get(key);
    if (cancel) {
      cancel();
      this.remove(key);
    }
  }

  /**
   * 取消所有请求
   */
  cancelAll() {
    this.pendingRequests.forEach((cancel) => cancel());
    this.pendingRequests.clear();
  }
}

const cancelManager = new CancelManager();

// ==================== 全局状态管理 ====================

// 当前环境语言选项
const language = window.localStorage
  .getItem('language-option')
  ?.replace(/"/g, '');

// 下载进度管理
const addDownloadProgress = (progressEvent) => {
  if (progressEvent.lengthComputable) {
    window.isFileDownloading = progressEvent.loaded !== progressEvent.total;
  } else {
    window.isFileDownloading = false;
  }
};

// ==================== Axios实例配置 ====================

// 设置全局默认配置
axios.defaults.timeout = CONFIG.TIMEOUT;
axios.defaults.withCredentials = true;

// 创建axios实例
const axiosInstance = axios.create({
  baseURL: '',
  headers: CONFIG.HEADERS,
});

// ==================== 请求拦截器 ====================

axiosInstance.interceptors.request.use(
  (config) => {
    // 生成请求标识
    const requestKey = `${config.method}:${config.url}:${Date.now()}`;
    config.requestKey = requestKey;

    // 添加请求头
    config.headers.Feign = 'feign';

    // CSRF防护
    const csrftoken = localStorage.getItem('csrftoken');
    if (csrftoken) {
      config.headers.forgerydefense = csrftoken.replace(/"/g, '');
    }

    // 国际化支持
    config.headers['language-option'] = language || 'zh-CN';

    // 用户标识
    config.headers['logName'] = localStorage.getItem('userid');

    // 处理GET请求参数
    if (config.method === 'get' && config.params) {
      config.params = processParams(config.params);
    }

    // 处理POST请求数据
    if (config.method === 'post' && config.data) {
      config.data = processParams(config.data);
    }

    // 添加取消令牌
    const cancelToken = new axios.CancelToken((cancel) => {
      cancelManager.add(requestKey, cancel);
    });
    config.cancelToken = cancelToken;

    // 请求日志
    console.log(`🚀 发起请求: ${config.method?.toUpperCase()} ${config.url}`, {
      params: config.params,
      data: config.data,
      headers: config.headers,
    });

    return config;
  },
  (error) => {
    console.error('❌ 请求拦截器错误:', error);
    return Promise.reject(error);
  }
);

// ==================== 响应拦截器 ====================

axiosInstance.interceptors.response.use(
  (response) => {
    const { config, status, data } = response;

    // 移除请求标识
    cancelManager.remove(config.requestKey);

    // 响应日志
    console.log(`✅ 请求成功: ${config.method?.toUpperCase()} ${config.url}`, {
      status,
      data,
    });

    // 处理成功响应
    if (status === CONFIG.SUCCESS_CODE) {
      return response;
    }

    return Promise.reject(new Error(`HTTP ${status}`));
  },
  (error) => {
    // 移除请求标识
    if (error.config?.requestKey) {
      cancelManager.remove(error.config.requestKey);
    }

    // 请求被取消
    if (axios.isCancel(error)) {
      console.log('🚫 请求被取消:', error.message);
      return Promise.reject(error);
    }

    // 响应错误处理
    if (error.response) {
      const { status, data, config } = error.response;

      console.error(
        `❌ 请求失败: ${config?.method?.toUpperCase()} ${config?.url}`,
        {
          status,
          data,
          error: error.message,
        }
      );

      // 根据状态码处理不同错误
      switch (status) {
        case CONFIG.UNAUTHORIZED:
          // 未授权,跳转到登录页
          console.warn('⚠️ 用户未授权,请重新登录');
          // 可以在这里添加跳转登录的逻辑
          break;

        case CONFIG.FORBIDDEN:
          console.warn('⚠️ 访问被禁止');
          break;

        case CONFIG.NOT_FOUND:
          console.warn('⚠️ 请求的资源不存在');
          break;

        case CONFIG.SERVER_ERROR:
          console.warn('⚠️ 服务器内部错误');
          break;

        default:
          console.warn(`⚠️ 请求失败,状态码: ${status}`);
      }

      return Promise.reject(data || error);
    }

    // 网络错误
    if (error.request) {
      console.error('🌐 网络连接失败:', error.message);
      return Promise.reject(new Error('网络连接失败,请检查网络设置'));
    }

    // 其他错误
    console.error('❌ 请求配置错误:', error.message);
    return Promise.reject(error);
  }
);

// ==================== 核心请求函数 ====================

/**
 * 核心请求函数
 * @param {Object} config - 请求配置
 * @param {Object} options - 额外选项
 * @returns {Promise} 请求Promise
 */
const request = async (config, options = {}) => {
  const {
    retry = CONFIG.RETRY_TIMES, // 重试次数
    retryDelay = CONFIG.RETRY_DELAY, // 重试延迟
    cache = false, // 是否启用缓存
    cacheExpire = CONFIG.CACHE_EXPIRE, // 缓存过期时间
    showLoading = false, // 是否显示加载状态
    responseFormat = 'standard', // 响应格式: 'standard' | 'direct' | 'auto'
  } = options;

  // 处理下载进度
  if (config.responseType === 'blob') {
    if (!config.onDownloadProgress) {
      config.onDownloadProgress = addDownloadProgress;
    }
  }

  // 缓存处理
  if (cache && config.method?.toLowerCase() === 'get') {
    const cacheKey = generateCacheKey(config);
    const cachedData = requestCache.get(cacheKey);
    if (cachedData) {
      console.log('📦 使用缓存数据:', cacheKey);
      return cachedData;
    }
  }

  // 显示加载状态
  let loadingInstance = null;
  if (showLoading) {
    // 这里可以集成Element UI的loading组件
    // loadingInstance = this.$loading({ text: loadingText });
  }

  try {
    const response = await axiosInstance.request(config);
    let result;

    // 根据响应格式处理数据
    if (response.data instanceof Blob) {
      // 文件下载,直接返回response
      result = response;
    } else if (responseFormat === 'direct') {
      // 直接返回数据,不处理标准格式
      result = response.data;
    } else if (responseFormat === 'auto') {
      // 自动判断:如果有code字段且为0,按标准格式处理;否则直接返回数据
      if (
        response.data &&
        typeof response.data === 'object' &&
        'code' in response.data
      ) {
        if (response.data.code === 0) {
          result =
            response.data.data !== undefined
              ? response.data.data
              : response.data;
        } else {
          throw new Error(response.data.message || '请求失败');
        }
      } else {
        // 直接返回数组或对象
        result = response.data;
      }
    } else {
      // 标准格式处理
      result = response.data;
    }

    // 缓存结果
    if (cache && config.method?.toLowerCase() === 'get') {
      const cacheKey = generateCacheKey(config);
      requestCache.set(cacheKey, result, cacheExpire);
    }

    return result;
  } catch (error) {
    // 重试逻辑
    if (retry > 0 && shouldRetry(error)) {
      console.log(
        `🔄 请求失败,${retryDelay}ms后重试,剩余重试次数: ${retry - 1}`
      );
      await delay(retryDelay);
      return request(config, { ...options, retry: retry - 1 });
    }

    throw error;
  } finally {
    // 隐藏加载状态
    if (loadingInstance) {
      // loadingInstance.close();
    }
  }
};

// ==================== 便捷方法 ====================

/**
 * GET请求
 * @param {Object} config - 请求配置
 * @param {Object} options - 额外选项
 * @returns {Promise} 请求Promise
 */
export function get(config, options = {}) {
  return request({ ...config, method: 'GET' }, options);
}

/**
 * POST请求
 * @param {Object} config - 请求配置
 * @param {Object} options - 额外选项
 * @returns {Promise} 请求Promise
 */
export function post(config, options = {}) {
  return request({ ...config, method: 'POST' }, options);
}

/**
 * PUT请求
 * @param {Object} config - 请求配置
 * @param {Object} options - 额外选项
 * @returns {Promise} 请求Promise
 */
export function put(config, options = {}) {
  return request({ ...config, method: 'PUT' }, options);
}

/**
 * DELETE请求
 * @param {Object} config - 请求配置
 * @param {Object} options - 额外选项
 * @returns {Promise} 请求Promise
 */
export function del(config, options = {}) {
  return request({ ...config, method: 'DELETE' }, options);
}

/**
 * 文件上传
 * @param {Object} config - 请求配置
 * @param {Object} options - 额外选项
 * @returns {Promise} 请求Promise
 */
export function upload(config, options = {}) {
  const uploadConfig = {
    ...config,
    method: 'POST',
    headers: {
      'Content-Type': 'multipart/form-data',
      ...config.headers,
    },
  };

  return request(uploadConfig, options);
}

/**
 * 文件下载
 * @param {Object} config - 请求配置
 * @param {Object} options - 额外选项
 * @returns {Promise} 请求Promise
 */
export function download(config, options = {}) {
  const downloadConfig = {
    ...config,
    responseType: 'blob',
    onDownloadProgress: addDownloadProgress,
  };

  return request(downloadConfig, options);
}

// ==================== 工具方法 ====================

/**
 * 取消指定请求
 * @param {string} key - 请求标识
 */
export function cancelRequest(key) {
  cancelManager.cancel(key);
}

/**
 * 取消所有请求
 */
export function cancelAllRequests() {
  cancelManager.cancelAll();
}

/**
 * 清除缓存
 * @param {string} key - 缓存键(可选)
 */
export function clearCache(key) {
  requestCache.clear(key);
}

/**
 * 设置请求超时时间
 * @param {number} timeout - 超时时间(ms)
 */
export function setTimeout(timeout) {
  axiosInstance.defaults.timeout = timeout;
}

/**
 * 设置请求头
 * @param {Object} headers - 请求头
 */
export function setHeaders(headers) {
  Object.assign(axiosInstance.defaults.headers, headers);
}

// ==================== 导出 ====================

export default request;

// 导出配置常量
export { CONFIG };

A. 智能重试机制

javascript 复制代码
// 自动重试失败的请求
const data = await getUsers('admin', {
  retry: 3,           // 重试3次
  retryDelay: 2000,   // 每次间隔2秒
});

// 重试条件:网络错误、5xx服务器错误
if (retry > 0 && shouldRetry(error)) {
  await delay(retryDelay);
  return request(config, { ...options, retry: retry - 1 });
}

B. 智能缓存系统

javascript 复制代码
// 自动缓存GET请求
const clouds = await getClouds({
  cache: true,                    // 启用缓存
  cacheExpire: 10 * 60 * 1000,   // 缓存10分钟
});

// 缓存键自动生成
const cacheKey = `${method}:${url}:${JSON.stringify(params)}:${JSON.stringify(data)}`;

C. 多格式响应支持

javascript 复制代码
// 标准格式:{code: 0, data: xxx}
const result = await addSalt(params);

// 直接格式:直接返回数据
const data = await getUsers('admin', { responseFormat: 'direct' });

// 自动格式:智能判断
const permissions = await getOperations(); // 自动处理权限数组

D. 请求取消功能

javascript 复制代码
// 取消单个请求
import { cancelRequest } from '@/utils/http/axios';
cancelRequest('requestKey');

// 取消所有请求
import { cancelAllRequests } from '@/utils/http/axios';
cancelAllRequests();

// 组件销毁时自动取消
export default {
  beforeDestroy() {
    cancelAllRequests();
  }
}

E. 文件下载优化

javascript 复制代码
// 专门的下载方法
import { download } from '@/utils/http/axios';

const blob = await download({
  url: '/api/download/file.pdf',
  method: 'post',
  data: { fileId: '123' }
}, {
  retry: 1,
  retryDelay: 2000,
  showLoading: true
});
  1. 使用方法详解
    A. 基础使用
javascript 复制代码
import { get, post, put, del } from '@/utils/http/axios';

// GET请求
const data = await get({
  url: '/api/users',
  params: { page: 1, size: 10 }
});

// POST请求
const result = await post({
  url: '/api/users',
  data: { name: 'John', age: 30 }
});

B. 高级配置

javascript 复制代码
// 带重试和缓存的请求
const data = await get({
  url: '/api/config',
}, {
  retry: 3,                    // 重试3次
  retryDelay: 1000,            // 重试间隔1秒
  cache: true,                 // 启用缓存
  cacheExpire: 5 * 60 * 1000,  // 缓存5分钟
  responseFormat: 'auto',      // 自动响应格式
  showLoading: true            // 显示加载状态
});

C. 文件操作

javascript 复制代码
import { upload, download } from '@/utils/http/axios';

// 文件上传
const formData = new FormData();
formData.append('file', file);
const result = await upload({
  url: '/api/upload',
  data: formData
});

// 文件下载
const blob = await download({
  url: '/api/download',
  method: 'post',
  data: { fileId: '123' }
}, {
  showLoading: true,
  loadingText: '文件下载中...'
});

D. 错误处理

javascript 复制代码
try {
  const data = await getUsers('admin');
} catch (error) {
  if (error.response) {
    // 服务器错误
    console.error('服务器错误:', error.response.status);
  } else if (error.request) {
    // 网络错误
    console.error('网络连接失败');
  } else {
    // 其他错误
    console.error('请求配置错误:', error.message);
  }
}
  1. API层使用示例
    A. 标准业务接口
javascript 复制代码
// service.js
export function addSalt(params, options = {}) {
  return post({
    url: `/api/collect-server/v1/cfg/addSalt`,
    data: params,
  }, { ...DEFAULT_OPTIONS, ...options })
    .then(response => handleResponse(response, true));
}

// 使用
const result = await addSalt(saltData, {
  retry: 2,
  showLoading: true
});

B. 配置类接口

javascript 复制代码
// service.js
export function getClouds(options = {}) {
  return get({
    url: `/api/collect-server/v1/cfg/getClouds`,
  }, { ...DEFAULT_OPTIONS, ...CACHE_OPTIONS, responseFormat: 'auto', ...options });
}

// 使用 - 自动缓存,直接返回数组
const clouds = await getClouds();

C. 权限接口

javascript 复制代码
// common-plat.js
export function getOperations(options = {}) {
  return post({
    url: `/api/oauth2/v1/perms/user/operations`,
    data: { operations: permission },
  }, { ...DEFAULT_OPTIONS, ...CACHE_OPTIONS, responseFormat: 'auto', ...options });
}

// 使用 - 自动处理权限数组
const permissions = await getOperations();
  1. 性能优化效果
    A. 缓存优化
javascript 复制代码
// 配置接口自动缓存5分钟
const clouds = await getClouds(); // 第一次请求
const clouds2 = await getClouds(); // 使用缓存,不发送请求

B. 重试优化

javascript 复制代码
// 网络不稳定时自动重试
const data = await getUsers('admin', { retry: 3 });
// 如果第一次失败,会自动重试3次

C. 请求取消

javascript 复制代码
// 避免重复请求和内存泄漏
// 页面切换时自动取消未完成的请求
  1. 总结
    优化后的axios封装提供了:
    ✅ 完整的错误处理 - 网络错误、服务器错误、业务错误
    ✅ 智能重试机制 - 自动重试失败的请求
    ✅ 智能缓存系统 - 自动缓存配置类接口
    ✅ 多格式响应支持 - 标准格式、直接格式、自动格式
    ✅ 请求取消功能 - 避免重复请求和内存泄漏
    ✅ 文件操作优化 - 专门的上传下载方法
    ✅ 详细日志记录 - 便于调试和监控
    ✅ 性能优化 - 缓存、重试、取消等机制
    ✅ 向后兼容 - 现有代码无需修改
    ✅ 易于扩展 - 模块化设计,便于添加新功能
    这个封装大大提升了开发效率和用户体验,是现代Web应用HTTP请求处理的最佳实践!

使用方法

xios封装使用方法大全

  1. 基础HTTP方法使用
    GET请求
javascript 复制代码
import { get } from '@/utils/http/axios';

// 简单GET请求
const data = await get({
  url: '/api/users',
  params: { page: 1, size: 10 }
});

// 带配置的GET请求
const data = await get({
  url: '/api/config',
  params: { type: 'system' }
}, {
  retry: 3,
  cache: true,
  cacheExpire: 10 * 60 * 1000
});

POST请求

javascript 复制代码
import { post } from '@/utils/http/axios';

// 简单POST请求
const result = await post({
  url: '/api/users',
  data: { name: 'John', age: 30 }
});

// 带配置的POST请求
const result = await post({
  url: '/api/users',
  data: userData
}, {
  retry: 2,
  showLoading: true
});

PUT请求

javascript 复制代码
import { put } from '@/utils/http/axios';

const result = await put({
  url: '/api/users/123',
  data: { name: 'John Updated' }
}, {
  retry: 1
});

DELETE请求

javascript 复制代码
import { del } from '@/utils/http/axios';

const result = await del({
  url: '/api/users/123'
}, {
  retry: 1
});
  1. 文件操作使用
    文件上传
javascript 复制代码
import { upload } from '@/utils/http/axios';

// 单文件上传
const formData = new FormData();
formData.append('file', file);
const result = await upload({
  url: '/api/upload',
  data: formData
}, {
  showLoading: true
});

// 多文件上传
const formData = new FormData();
files.forEach(file => {
  formData.append('files', file);
});
const result = await upload({
  url: '/api/upload/multiple',
  data: formData
});

文件下载

javascript 复制代码
import { download } from '@/utils/http/axios';

// 简单下载
const blob = await download({
  url: '/api/download/file.pdf',
  method: 'get'
});

// 带参数的下载
const blob = await download({
  url: '/api/download',
  method: 'post',
  data: { fileId: '123' }
}, {
  showLoading: true,
  retry: 1
});

// 下载并保存文件
const blob = await download({
  url: '/api/download/report.xlsx',
  method: 'post',
  data: { reportId: '456' }
});

// 创建下载链接
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'report.xlsx';
link.click();
window.URL.revokeObjectURL(url);
  1. 响应格式使用
    标准格式(默认)
javascript 复制代码
// 返回 {code: 0, data: xxx, message: 'success'}
const result = await post({
  url: '/api/users',
  data: userData
}, {
  responseFormat: 'standard' // 默认值
});

if (result.code === 0) {
  const data = result.data;
}

直接格式

javascript 复制代码
// 直接返回 response.data
const data = await get({
  url: '/api/config'
}, {
  responseFormat: 'direct'
});

自动格式(推荐)

javascript 复制代码
// 智能判断:有code字段按标准格式处理,否则直接返回数据
const permissions = await get({
  url: '/api/permissions'
}, {
  responseFormat: 'auto'
});

// 如果返回 {code: 0, data: [...]} 则自动提取 data
// 如果直接返回 [...] 则直接使用
  1. 缓存使用
    启用缓存
javascript 复制代码
// 自动缓存GET请求
const data = await get({
  url: '/api/config'
}, {
  cache: true,
  cacheExpire: 5 * 60 * 1000 // 缓存5分钟
});

// 第二次调用会使用缓存
const cachedData = await get({
  url: '/api/config'
}, {
  cache: true
});

清除缓存

javascript 复制代码
import { clearCache } from '@/utils/http/axios';

// 清除所有缓存
clearCache();

// 清除特定缓存
clearCache('get:/api/config');
  1. 重试机制使用
    基础重试
javascript 复制代码
const data = await get({
  url: '/api/users'
}, {
  retry: 3,           // 重试3次
  retryDelay: 1000    // 每次间隔1秒
});

不同场景的重试配置

javascript 复制代码
// 重要数据,多重重试
const userData = await get({
  url: '/api/user/profile'
}, {
  retry: 5,
  retryDelay: 2000
});

// 文件下载,少量重试
const blob = await download({
  url: '/api/download/file'
}, {
  retry: 1,
  retryDelay: 3000
});

// 配置数据,不重试
const config = await get({
  url: '/api/config'
}, {
  retry: 0,
  cache: true
});
  1. 请求取消使用
    取消单个请求
javascript 复制代码
import { cancelRequest } from '@/utils/http/axios';

// 发起请求
const requestKey = `get:/api/users:${Date.now()}`;
const data = await get({
  url: '/api/users',
  requestKey
});

// 取消请求
cancelRequest(requestKey);

取消所有请求

javascript 复制代码
import { cancelAllRequests } from '@/utils/http/axios';

// 在组件销毁时取消所有请求
export default {
  beforeDestroy() {
    cancelAllRequests();
  }
}

// 在页面切换时取消
window.addEventListener('beforeunload', () => {
  cancelAllRequests();
});
  1. 错误处理使用
    try-catch处理
javascript 复制代码
try {
  const data = await get({
    url: '/api/users'
  });
} catch (error) {
  if (error.response) {
    // 服务器错误
    console.error('服务器错误:', error.response.status);
  } else if (error.request) {
    // 网络错误
    console.error('网络连接失败');
  } else {
    // 其他错误
    console.error('请求配置错误:', error.message);
  }
}

Promise处理

javascript 复制代码
get({
  url: '/api/users'
}).then(data => {
  console.log('成功:', data);
}).catch(error => {
  console.error('失败:', error);
});
  1. API层使用示例
    业务接口调用
javascript 复制代码
import { getUsers, addSalt, downloadFile } from '@/api/service';

// 获取用户列表(带缓存)
const users = await getUsers('admin', false, {
  cache: true,
  cacheExpire: 10 * 60 * 1000
});

// 添加Salt(带重试)
const result = await addSalt(saltData, {
  retry: 3,
  showLoading: true
});

// 下载文件(带进度)
const blob = await downloadFile(subTaskId, {
  showLoading: true
});

权限接口调用

javascript 复制代码
import { getOperations, getUserName } from '@/api/common-plat';

// 获取权限(自动格式)
const permissions = await getOperations();

// 获取用户名(自动格式)
const username = await getUserName();
  1. 高级配置使用
    自定义配置
javascript 复制代码
// 全局配置
import { setTimeout, setHeaders } from '@/utils/http/axios';

// 设置超时时间
setTimeout(30000);

// 设置请求头
setHeaders({
  'Custom-Header': 'value',
  'Authorization': 'Bearer token'
});

组合配置

javascript 复制代码
const advancedOptions = {
  retry: 3,
  retryDelay: 2000,
  cache: true,
  cacheExpire: 10 * 60 * 1000,
  responseFormat: 'auto',
  showLoading: true
};

const data = await get({
  url: '/api/important-data'
}, advancedOptions);
  1. 实际项目使用场景
    页面初始化
javascript 复制代码
export default {
  async mounted() {
    try {
      // 并行加载配置数据
      const [clouds, permissions, userInfo] = await Promise.all([
        getClouds({ cache: true }),
        getOperations({ cache: true }),
        getUserName({ cache: true })
      ]);
      
      this.clouds = clouds;
      this.permissions = permissions;
      this.userInfo = userInfo;
    } catch (error) {
      this.$message.error('初始化失败');
    }
  }
}

表单提交

javascript 复制代码
async submitForm() {
  try {
    this.loading = true;
    const result = await addSalt(this.formData, {
      retry: 2,
      showLoading: false // 使用自定义loading
    });
    
    this.$message.success('添加成功');
    this.$emit('success');
  } catch (error) {
    this.$message.error(error.message || '添加失败');
  } finally {
    this.loading = false;
  }
}

文件下载

javascript 复制代码
async downloadReport() {
  try {
    const blob = await downloadFile(this.reportId, {
      showLoading: true
    });
    
    // 保存文件
    const url = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = `report_${Date.now()}.xlsx`;
    link.click();
    window.URL.revokeObjectURL(url);
    
    this.$message.success('下载成功');
  } catch (error) {
    this.$message.error('下载失败');
  }
}
  1. 最佳实践总结
    配置建议
javascript 复制代码
// 配置类接口:启用缓存,不重试
const configOptions = {
  cache: true,
  cacheExpire: 5 * 60 * 1000,
  retry: 0
};

// 业务接口:启用重试,不缓存
const businessOptions = {
  retry: 2,
  retryDelay: 1000,
  cache: false
};

// 文件操作:少量重试,显示进度
const fileOptions = {
  retry: 1,
  retryDelay: 2000,
  showLoading: true
};

错误处理建议

javascript 复制代码
// 统一错误处理
const handleApiError = (error) => {
  if (error.response?.status === 401) {
    // 跳转登录
    router.push('/login');
  } else if (error.response?.status === 403) {
    this.$message.error('权限不足');
  } else {
    this.$message.error(error.message || '操作失败');
  }
};

这些使用方法涵盖了axios封装的所有功能,可以根据具体需求选择合适的配置和调用方式!

相关推荐
奕辰杰1 小时前
关于npm前端项目编译时栈溢出 Maximum call stack size exceeded的处理方案
前端·npm·node.js
JiaLin_Denny3 小时前
如何在NPM上发布自己的React组件(包)
前端·react.js·npm·npm包·npm发布组件·npm发布包
_Kayo_4 小时前
VUE2 学习笔记14 nextTick、过渡与动画
javascript·笔记·学习
路光.4 小时前
触发事件,按钮loading状态,封装hooks
前端·typescript·vue3hooks
我爱996!4 小时前
SpringMVC——响应
java·服务器·前端
咔咔一顿操作5 小时前
Vue 3 入门教程7 - 状态管理工具 Pinia
前端·javascript·vue.js·vue3
天若有情6735 小时前
【python】Python爬虫入门教程:使用requests库
开发语言·爬虫·python·网络爬虫·request
kk爱闹5 小时前
用el-table实现的可编辑的动态表格组件
前端·vue.js
寒水馨6 小时前
Java 17 新特性解析与代码示例
java·开发语言·jdk17·新特性·java17