JS Axios 封装最佳实践与实战指南

2025-12-27 4489阅读

在现代前端开发中,Axios已成为处理HTTP请求的主流工具之一。但直接使用Axios可能面临配置分散、错误处理重复、环境适配复杂等问题。本文将从基础到进阶,详解如何封装Axios,提升项目的可维护性与扩展性。

一、Axios封装的核心价值

Axios本身是功能强大的HTTP客户端,但在复杂项目中,直接使用其默认API会导致以下问题:

  • 不同接口的基础URL、超时时间、请求头等配置分散
  • 错误处理逻辑重复书写
  • 缺乏请求重试、取消等高级功能
  • 难以适配开发/生产环境切换

封装的核心目标:通过统一配置、拦截器和功能扩展,实现请求逻辑的复用与标准化,让业务代码更简洁。

二、基础封装实现

1. 核心配置与拦截器

以下是最基础的Axios封装,包含默认配置、请求拦截、响应拦截和错误处理:

import axios from 'axios';

class AxiosService {
  constructor(config = {}) {
    // 1. 创建Axios实例,配置基础参数
    this.instance = axios.create({
      baseURL: config.baseURL || 'https://api.example.com',
      timeout: config.timeout || 5000,
      headers: {
        'Content-Type': 'application/json',
        ...config.headers
      }
    });

    // 2. 初始化拦截器
    this.setupInterceptors();
  }

  // 2.1 请求拦截器:添加token、统一参数格式
  setupInterceptors() {
    this.instance.interceptors.request.use(
      (config) => {
        // 示例:从localStorage获取token
        const token = localStorage.getItem('token');
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      (error) => {
        // 请求错误直接抛出
        return Promise.reject(error);
      }
    );

    // 2.2 响应拦截器:统一数据格式、错误分类
    this.instance.interceptors.response.use(
      (response) => {
        // 假设接口返回格式为 { code, data, msg }
        const { data } = response;
        if (data.code === 200) {
          return data.data; // 仅返回业务数据
        } else {
          console.error(`请求错误: ${data.msg}`);
          return Promise.reject(new Error(data.msg));
        }
      },
      (error) => {
        const { response } = error;
        if (!response) {
          // 网络错误
          return Promise.reject(new Error('网络连接失败,请检查网络设置'));
        }
        // 错误状态码分类处理
        switch (response.status) {
          case 401:
            // Token过期,跳转登录页
            localStorage.removeItem('token');
            window.location.href = '/login';
            break;
          case 403:
            return Promise.reject(new Error('权限不足'));
          case 500:
            return Promise.reject(new Error('服务器内部错误'));
          default:
            return Promise.reject(new Error(`请求错误: ${response.status}`));
        }
      }
    );
  }

  // 3. 封装请求方法
  get(url, params = {}) {
    return this.instance.get(url, { params });
  }

  post(url, data = {}) {
    return this.instance.post(url, data);
  }

  put(url, data = {}) {
    return this.instance.put(url, data);
  }

  delete(url, params = {}) {
    return this.instance.delete(url, { params });
  }
}

export default AxiosService;

三、进阶功能扩展

1. 请求重试机制

针对5xx错误或网络波动,实现自动重试功能:

class AxiosService {
  constructor(config = {}) {
    this.instance = axios.create({...});
    this.retryCount = config.retryCount || 3; // 重试次数
    this.retryDelay = config.retryDelay || 1000; // 重试间隔(ms)
    this.setupInterceptors();
  }

  setupInterceptors() {
    this.instance.interceptors.response.use(
      // ...基础拦截器逻辑
      (error) => {
        const { response, config } = error;
        // 判断是否需要重试(5xx错误或网络错误)
        if (this.shouldRetry(response, error) && this.retryCount > 0) {
          this.retryCount--;
          // 延迟后重试请求
          return new Promise(resolve => {
            setTimeout(() => {
              resolve(this.instance(config));
            }, this.retryDelay);
          });
        }
        return Promise.reject(error);
      }
    );
  }

  shouldRetry(response, error) {
    // 网络错误或5xx状态码触发重试
    return !response || 
           (response.status >= 500 && response.status < 600) || 
           error.message.includes('NetworkError');
  }
}

2. 取消请求功能

防止重复请求或组件卸载时残留请求:

class AxiosService {
  constructor(config = {}) {
    this.instance = axios.create({...});
    this.cancelTokenMap = new Map(); // 存储取消令牌
    this.setupInterceptors();
  }

  // 发起请求时生成取消令牌
  get(url, params = {}, cancelKey = null) {
    const controller = new AbortController();
    const signal = controller.signal;
    if (cancelKey) {
      this.cancelTokenMap.set(cancelKey, controller);
    }
    return this.instance.get(url, { params, signal });
  }

  // 取消指定请求
  cancelRequest(cancelKey) {
    const controller = this.cancelTokenMap.get(cancelKey);
    if (controller) {
      controller.abort();
      this.cancelTokenMap.delete(cancelKey);
    }
  }

  // 组件卸载时取消所有请求
  cancelAllRequests() {
    this.cancelTokenMap.forEach(controller => controller.abort());
    this.cancelTokenMap.clear();
  }
}

3. 环境配置适配

区分开发/生产环境的API地址:

// 环境配置
const envConfig = {
  development: {
    baseURL: 'http://localhost:3000/api',
    timeout: 5000
  },
  production: {
    baseURL: 'https://api.example.com',
    timeout: 10000
  }
};

// 实例化时传入环境参数
const api = new AxiosService({
  baseURL: envConfig[process.env.NODE_ENV || 'development'].baseURL,
  timeout: envConfig[process.env.NODE_ENV || 'development'].timeout
});

四、实战应用场景

1. 在Vue项目中使用

// main.js
import Vue from 'vue';
import App from './App.vue';
import AxiosService from './axiosService';

// 实例化封装后的Axios
const api = new AxiosService({
  baseURL: process.env.NODE_ENV === 'production' 
    ? 'https://prod-api.com' 
    : 'https://dev-api.com'
});

// 挂载到Vue原型
Vue.prototype.$api = api;

new Vue({
  render: h => h(App)
}).$mount('#app');

// 组件中使用
export default {
  methods: {
    async fetchUser() {
      try {
        const user = await this.$api.get('/user', { id: 1 });
        this.userData = user;
      } catch (err) {
        console.error('获取用户失败', err);
      }
    }
  }
};

2. 在React项目中使用


// useApi.js 自定义Hook
import { useState, useEffect } from 'react';
import AxiosService from './axiosService';

export function useApi() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const api = new AxiosService();

  const request = async (method, url, params = {}) => {
    setLoading(true);
    try {
      const result = await api[method](url, params);
      setData(result);
      return result;
文章版权声明:除非注明,否则均为Dark零点博客原创文章,转载或复制请以超链接形式并注明出处。

目录[+]