给vue2,3和react搭建的后天管理系统封装 axios

axios封装

最近在搭建vue3和react的后台管系统项目,两个项目中都使用axios,所以对axios进行了个比较全的封装,方便使用,下面是封装axios的思路,希望大家补足没考虑到的地方。

下载axios

css 复制代码
npm install axios --save

封装axios

1. 创建axios实例

引入axios,创建axios实例,设置请求的baseURL、请求超时时间、请求头等。

javascript 复制代码
import axios from 'axios';
const http = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 15000,//请求超时设置
  headers: { 'Content-Type': 'application/json' }//默认请求参数格式json
});
2. 添加请求拦截器

在请求拦截器中,我们可以对请求进行一些统一的处理,例如添加请求头、添加请求参数、添加请求标识、对请求体加密等。

javascript 复制代码
http.interceptors.request.use(
  (config) => {
        // 取消重复请求
    removePendingRequest(config);
    addPendingRequest(config);
    
    // 自动添加token
    const token =localStorage.getItem('token')
    if (token && !config.headers.Authorization) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    
    // 全局loading控制(需配合状态管理)
    // store.dispatch(showGlobalLoading());
    
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);
3. 添加响应拦截器

在响应拦截器中,我们可以对响应进行一些统一的处理,例如对token过期处理、对响应数据进行处理、对响应状态码进行统一处理、对响应错误进行统一处理等。

javascript 复制代码
http.interceptors.response.use(
  (response) => {
    // 移除请求队列
    removePendingRequest(response.config);
    
    // 关闭全局loading
    // store.dispatch(hideGlobalLoading());
    
    // 处理业务状态码(根据后端协议调整)
    const { code, data } = response.data;
    if (code !== 200) {
      if (code === 401) {
        // token过期处理
      localStorage.removeItem('token')
        window.location.href = '/login';//跳到登录
      }
      ElMessage.error(data.message || '操作失败');
      return Promise.reject(new Error(data.message ));
    }
    
    return response.data;
  },
  (error) => {
    // 关闭loading
    // store.dispatch(hideGlobalLoading());
    
    // 处理取消的请求
    if (axios.isCancel(error)) {
     
      return Promise.reject(error);
    }
    
    // 错误处理
    let errorMessage = '请求失败';
    if (error.response) {
      switch (error.response.status) {
        //常规状态码对应错误提示
        case 401:
          errorMessage = '登录已过期,请重新登录';
          localStorage.removeItem('token')
          window.location.href = '/login';
          break;
        case 403:
          errorMessage = '没有操作权限';
          break;
        case 500:
          errorMessage = '服务器异常';
          break;
      }
    } else if (error.message.includes('timeout')) {
      errorMessage = '请求超时';
    } else if (error.message.includes('Network Error')) {
      errorMessage = '网络连接失败';
    }
    
  ElMessage.error(errorMessage);
    return Promise.reject(error);
  }
);
4. 创建请求队列

创建请求队列,用于存储待处理的请求,以便在请求被取消时进行相应的处理,对重复请求进行取消以提高性能,降低服务端荷载

javascript 复制代码
// 请求队列
const pendingRequests = new Map();
5.添加请求到队列的方法

定义一个函数 addPendingRequest,用于添加待处理的请求,将其添加到请求队列中

arduino 复制代码
// 添加请求到队列
// 定义一个函数 addPendingRequest,用于添加待处理的请求
const addPendingRequest = (config) => {
  // 调用 generateReqKey 函数生成请求的唯一标识符
  const requestKey = generateReqKey(config);
  // 为 config 对象设置 cancelToken 属性,如果它不存在则创建一个新的 axios.CancelToken 实例
  config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
    // 检查 pendingRequests 中是否已经存在该请求标识符
    if (!pendingRequests.has(requestKey)) {
      // 如果不存在,则将请求标识符和取消函数存储到 pendingRequests 中
      pendingRequests.set(requestKey, cancel);
    }
  });
};
6.移除请求的方法

定义一个名为 removePendingRequest 的函数,将请求从请求队列中移除,

scss 复制代码
// 移除请求
// 定义一个名为 removePendingRequest 的函数,用于移除待处理的请求
const removePendingRequest = (config) => {
  // 调用 generateReqKey 函数生成请求的唯一标识符
  const requestKey = generateReqKey(config);
  // 检查 pendingRequests 集合中是否包含当前请求的唯一标识符
  if (pendingRequests.has(requestKey)) {
    // 如果存在,则获取对应的取消请求的函数
    const cancel = pendingRequests.get(requestKey);
    // 调用取消请求的函数,并传入请求的唯一标识符
    cancel(requestKey);
    // 从 pendingRequests 集合中删除该请求的唯一标识符
    pendingRequests.delete(requestKey);
  }
};
7.生成请求标识

生成请求的唯一标识符,用于在请求队列中查找和删除重复请求。

arduino 复制代码
// 生成请求标识
// 定义一个名为 generateReqKey 的函数,用于生成请求的唯一标识符
const generateReqKey = (config) => {
  // 返回一个由多个部分组成的字符串,这些部分通过 '&' 符号连接
  return [
    // config.method 表示请求的方法,如 GET、POST 等
    config.method,
    // config.url 表示请求的 URL 地址
    config.url,
    // JSON.stringify(config.params) 将请求的参数对象转换为 JSON 字符串
    JSON.stringify(config.params),
    // JSON.stringify(config.data) 将请求的数据对象转换为 JSON 字符串
    JSON.stringify(config.data)
  ].join('&');
};
8.根据restful规范封装常用方法并导出

根据restful规范封装常用方法,例如get、post、put、patch、delete,upload等方法,并导出

javascript 复制代码
// 封装常用方法(支持自动重试)
export const request = {
  get: (url, params, config) => http.get(url, { params, ...config }),
  post: (url, data, config) => http.post(url, data, config),
  put: (url, data, config) => http.put(url, data, config),
  patch: (url, data, config) => http.patch(url, data, config),
  delete: (url, params, config) => http.delete(url, { params, ...config }),
  upload: (url, file, config) => {
    const formData = new FormData();
    formData.append('file', file);
    return http.post(url, formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
      ...config
    });
  },
  // 带自动重试的请求
  retry: async (fn, retries = 3, delay = 1000) => {
    try {
      return await fn();
    } catch (error) {
      return retries > 1 
        ? new Promise(resolve => 
            setTimeout(() => resolve(request.retry(fn, retries - 1, delay)), delay)
          )
        : Promise.reject(error);
    }
  }
};

完整代码

javascript 复制代码
import axios from 'axios';
import { ElMessage } from 'element-plus' // 使用element-plus消息提示


// 创建axios实例

const http = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 15000,//请求超时设置
  headers: { 'Content-Type': 'application/json' }
});

// 请求队列
const pendingRequests = new Map();

// 生成请求标识
// 定义一个名为 generateReqKey 的函数,用于生成请求的唯一标识符
const generateReqKey = (config) => {
  // 返回一个由多个部分组成的字符串,这些部分通过 '&' 符号连接
  return [
    // config.method 表示请求的方法,如 GET、POST 等
    config.method,
    // config.url 表示请求的 URL 地址
    config.url,
    // JSON.stringify(config.params) 将请求的参数对象转换为 JSON 字符串
    JSON.stringify(config.params),
    // JSON.stringify(config.data) 将请求的数据对象转换为 JSON 字符串
    JSON.stringify(config.data)
  ].join('&');
};

// 添加请求到队列
// 定义一个函数 addPendingRequest,用于添加待处理的请求
const addPendingRequest = (config) => {
  // 调用 generateReqKey 函数生成请求的唯一标识符
  const requestKey = generateReqKey(config);
  // 为 config 对象设置 cancelToken 属性,如果它不存在则创建一个新的 axios.CancelToken 实例
  config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
    // 检查 pendingRequests 中是否已经存在该请求标识符
    if (!pendingRequests.has(requestKey)) {
      // 如果不存在,则将请求标识符和取消函数存储到 pendingRequests 中
      pendingRequests.set(requestKey, cancel);
    }
  });
};

// 移除请求
// 定义一个名为 removePendingRequest 的函数,用于移除待处理的请求
const removePendingRequest = (config) => {
  // 调用 generateReqKey 函数生成请求的唯一标识符
  const requestKey = generateReqKey(config);
  // 检查 pendingRequests 集合中是否包含当前请求的唯一标识符
  if (pendingRequests.has(requestKey)) {
    // 如果存在,则获取对应的取消请求的函数
    const cancel = pendingRequests.get(requestKey);
    // 调用取消请求的函数,并传入请求的唯一标识符
    cancel(requestKey);
    // 从 pendingRequests 集合中删除该请求的唯一标识符
    pendingRequests.delete(requestKey);
  }
};

// 请求拦截器
http.interceptors.request.use(
  (config) => {
    // 取消重复请求
    removePendingRequest(config);
    addPendingRequest(config);
    
    // 自动添加token
    const token =localStorage.getItem('token')
    if (token && !config.headers.Authorization) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    
    // 全局loading控制(需配合状态管理)
    // store.dispatch(showGlobalLoading());
    
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 响应拦截器
http.interceptors.response.use(
  (response) => {
    // 移除请求队列
    removePendingRequest(response.config);
    
    // 关闭loading
    // store.dispatch(hideGlobalLoading());
    
    // 处理业务状态码(根据后端协议调整)
    const { code, data } = response.data;
    if (code !== 200) {
      if (code === 401) {
        // token过期处理
      localStorage.removeItem('token')
        window.location.href = '/login';
      }
      ElMessage.error(data.message || '操作失败');
      return Promise.reject(new Error(data.message ));
    }
    
    return response.data;
  },
  (error) => {
    // 关闭loading
    // store.dispatch(hideGlobalLoading());
    
    // 处理取消的请求
    if (axios.isCancel(error)) {
      console.log('请求被取消:', error.message);
      return Promise.reject(error);
    }
    
    // 错误处理
    let errorMessage = '请求失败';
    if (error.response) {
      switch (error.response.status) {
        case 401:
          errorMessage = '登录已过期,请重新登录';
          localStorage.removeItem('token')
          window.location.href = '/login';
          break;
        case 403:
          errorMessage = '没有操作权限';
          break;
        case 500:
          errorMessage = '服务器异常';
          break;
      }
    } else if (error.message.includes('timeout')) {
      errorMessage = '请求超时';
    } else if (error.message.includes('Network Error')) {
      errorMessage = '网络连接失败';
    }
    
  ElMessage.error(errorMessage);
    return Promise.reject(error);
  }
);

// 封装常用方法(支持自动重试)
export const request = {
  get: (url, params, config) => http.get(url, { params, ...config }),
  post: (url, data, config) => http.post(url, data, config),
  put: (url, data, config) => http.put(url, data, config),
  patch: (url, data, config) => http.patch(url, data, config),
  delete: (url, params, config) => http.delete(url, { params, ...config }),
  upload: (url, file, config) => {
    const formData = new FormData();
    formData.append('file', file);
    return http.post(url, formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
      ...config
    });
  },
  // 带自动重试的请求
  retry: async (fn, retries = 3, delay = 1000) => {
    try {
      return await fn();
    } catch (error) {
      return retries > 1 
        ? new Promise(resolve => 
            setTimeout(() => resolve(request.retry(fn, retries - 1, delay)), delay)
          )
        : Promise.reject(error);
    }
  }
};

// 自定义Hook方式封装
// 导出一个名为 useHttp 的常量,它是一个函数
export const useHttp = () => {
  // 可以在此集成全局loading状态管理
  return {
    ...request,
    // 扩展其他方法...
  };
}; 

总结:封装一个适用于大部分后台管理系统的axios,导出了get,post,put,patch,delete,puload等result规范常用的请求方式,自动重试的请求方法。处理了常见的状态码,取消连续发送的相同请求,提高性能,减少服务器负载。

相关推荐
anyup_前端梦工厂16 分钟前
React 单一职责原则:优化组件设计与提高可维护性
前端·javascript·react.js
天天扭码43 分钟前
面试官:算法题”除自身以外数组的乘积“ 我:😄 面试官:不能用除法 我:😓
前端·算法·面试
L2ncE1 小时前
双非计算机自救指南(找工作版)
后端·面试·程序员
小小小小宇1 小时前
十万字JS不良实践总结(逼疯审核版)
前端
喝拿铁写前端1 小时前
从列表页到规则引擎:一个组件封装过程中的前端认知进阶
前端·vue.js·架构
小小小小宇1 小时前
React Lanes(泳道)机制
前端
zhangxingchao1 小时前
Jetpack Compose 之 Modifier(上)
前端
龙萌酱1 小时前
力扣每日打卡17 49. 字母异位词分组 (中等)
前端·javascript·算法·leetcode
工呈士2 小时前
HTML与Web性能优化
前端·html
秃了才能变得更强2 小时前
React Native 原生模块集成Turbo Modules
前端