Axios详解

Axios详解

_

一、Axios 是什么?

Axios 是一个基于 Promise 的 HTTP 客户端,可以同时在 浏览器 和 Node.js 环境中使用。

// 最基础的用法
axios.get('/api/users')
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

二、Axios 的安装与引入

2.1 安装方式

# npm 安装(推荐)
npm install axios

# yarn 安装
yarn add axios

# CDN 引入
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

2.2 引入方式

// ES Module(现代项目)
import axios from 'axios';

// CommonJS(Node.js)
const axios = require('axios');

// 浏览器中直接使用
// <script> 标签引入后,全局有 axios 对象

三、Axios API

3.1 请求方法

// ===== 基础方法 =====
// GET 请求
axios.get('/api/users', {
  params: { page: 1, limit: 10 }
});

// POST 请求
axios.post('/api/users', {
  name: '张三',
  email: 'zhangsan@example.com'
});

// PUT 请求(全量更新)
axios.put('/api/users/1', {
  name: '张三更新',
  email: 'newemail@example.com'
});

// PATCH 请求(部分更新)
axios.patch('/api/users/1', {
  name: '张三'
});

// DELETE 请求
axios.delete('/api/users/1');

// HEAD 请求
axios.head('/api/users/1');

// OPTIONS 请求
axios.options('/api/users/1');

// ===== 通用请求方法 =====
axios({
  method: 'post',
  url: '/api/users',
  data: { name: '李四' }
});

// ===== 并发请求 =====
axios.all([
  axios.get('/api/users'),
  axios.get('/api/posts'),
  axios.get('/api/comments')
])
.then(axios.spread((users, posts, comments) => {
  console.log('用户:', users.data);
  console.log('文章:', posts.data);
  console.log('评论:', comments.data);
}));

// 或使用 Promise.all(更现代)
const [users, posts, comments] = await Promise.all([
  axios.get('/api/users'),
  axios.get('/api/posts'),
  axios.get('/api/comments')
]);

3.2 请求配置

const config = {
  // ----- 基础配置 -----
  url: '/api/users',           // 请求 URL(必须)
  method: 'get',               // 请求方法(默认 get)
  baseURL: 'https://api.example.com', // 基础 URL
  
  // ----- 数据配置 -----
  data: {                      // POST/PUT/PATCH 的请求体
    name: '张三'
  },
  params: {                    // URL 查询参数(GET 用)
    page: 1,
    limit: 10
  },
  paramsSerializer: (params) => { // 自定义参数序列化
    return JSON.stringify(params);
  },
  
  // ----- 头部配置 -----
  headers: {                   // 自定义请求头
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  },
  
  // ----- 响应配置 -----
  responseType: 'json',        // 响应类型:json/blob/stream/text/arraybuffer
  responseEncoding: 'utf8',    // 响应编码
  transformResponse: [(data) => { // 转换响应数据
    return JSON.parse(data);
  }],
  
  // ----- 超时与取消 -----
  timeout: 5000,               // 超时时间(毫秒)
  timeoutErrorMessage: '请求超时', // 超时错误信息
  cancelToken: new axios.CancelToken(c => { // 取消令牌
    cancelFn = c;
  }),
  
  // ----- 跨域与凭证 -----
  withCredentials: true,       // 跨域携带 Cookie
  xsrfCookieName: 'XSRF-TOKEN', // CSRF Token 的 Cookie 名
  xsrfHeaderName: 'X-XSRF-TOKEN', // CSRF Token 的 Header 名
  
  // ----- 上传/下载进度 -----
  onUploadProgress: (progressEvent) => {
    const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
    console.log(`上传进度: ${percent}%`);
  },
  onDownloadProgress: (progressEvent) => {
    const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
    console.log(`下载进度: ${percent}%`);
  },
  
  // ----- 其他高级配置 -----
  maxContentLength: 2000,      // 响应体最大长度(字节)
  maxBodyLength: 2000,         // 请求体最大长度(字节)
  maxRedirects: 5,             // 最大重定向次数
  socketPath: null,            // Unix Socket 路径
  httpAgent: new http.Agent(), // Node.js HTTP Agent
  httpsAgent: new https.Agent(), // Node.js HTTPS Agent
  proxy: {                     // 代理配置(Node.js)
    host: '127.0.0.1',
    port: 9000,
    auth: { username: 'user', password: 'pass' }
  },
  decompress: true,            // 自动解压响应数据
  transitional: {              // 过渡选项(处理废弃属性)
    silentJSONParsing: true,
    forcedJSONParsing: true,
    clarifyTimeoutError: false
  }
};

3.3 响应对象结构

const response = await axios.get('/api/users');

// 响应对象包含:
console.log(response.data);     // 服务器返回的数据
console.log(response.status);   // HTTP 状态码 (200)
console.log(response.statusText); // HTTP 状态文本 ('OK')
console.log(response.headers);  // 响应头对象
console.log(response.config);   // 本次请求的配置
console.log(response.request);  // 底层请求对象 (XMLHttpRequest 或 http.ClientRequest)

四、拦截器

4.1 拦截器的基本使用

// ===== 请求拦截器 =====
axios.interceptors.request.use(
  config => {
    // 在请求发送之前做些什么
    console.log('请求拦截器:', config);
    
    // 统一添加 Token
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    
    // 统一添加时间戳(防缓存)
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        _t: Date.now()
      };
    }
    
    return config;
  },
  error => {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// ===== 响应拦截器 =====
axios.interceptors.response.use(
  response => {
    // 对响应数据做点什么
    console.log('响应拦截器:', response);
    
    // 统一处理后端返回的数据结构
    if (response.data.code === 0) {
      return response.data.data; // 只返回业务数据
    } else {
      return Promise.reject(new Error(response.data.message));
    }
  },
  error => {
    // 对响应错误做点什么
    console.error('响应错误:', error);
    
    // 统一处理 401 未授权
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    
    // 统一错误提示
    if (error.response) {
      // 服务器返回了错误状态码
      const message = error.response.data?.message || '请求失败';
      showToast(message);
    } else if (error.request) {
      // 请求已发出但没有收到响应
      showToast('网络异常,请检查网络连接');
    } else {
      // 请求配置出错
      showToast('请求配置错误');
    }
    
    return Promise.reject(error);
  }
);

4.2 移除拦截器

// 为拦截器命名
const requestInterceptor = axios.interceptors.request.use(config => config);
const responseInterceptor = axios.interceptors.response.use(response => response);

// 移除特定的拦截器
axios.interceptors.request.eject(requestInterceptor);
axios.interceptors.response.eject(responseInterceptor);

4.3 实例级别的拦截器

// 创建独立的实例
const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000
});

// 为这个实例单独添加拦截器
apiClient.interceptors.request.use(config => {
  config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
  return config;
});

// 默认实例的拦截器不会影响 apiClient

4.4 拦截器的执行顺序

// 请求拦截器执行顺序:后添加的先执行(类似栈)
axios.interceptors.request.use(config => {
  console.log('请求拦截器 1');
  return config;
});
axios.interceptors.request.use(config => {
  console.log('请求拦截器 2'); // 先输出
  return config;
});

// 响应拦截器执行顺序:先添加的先执行
axios.interceptors.response.use(response => {
  console.log('响应拦截器 1'); // 先输出
  return response;
});
axios.interceptors.response.use(response => {
  console.log('响应拦截器 2');
  return response;
});

五、Axios 实例

创建独立的实例,适用于不同 API 服务或不同配置。

5.1 创建和配置实例

// 创建实例
const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json'
  }
});

// 配置实例的默认值
api.defaults.baseURL = 'https://api.example.com';
api.defaults.headers.common['Authorization'] = 'Bearer token';
api.defaults.headers.post['Content-Type'] = 'application/json';

// 为实例添加拦截器
api.interceptors.request.use(config => {
  config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
  return config;
});

// 使用实例
api.get('/users')
  .then(res => console.log(res.data))
  .catch(err => console.error(err));

5.2 多实例场景

// 主业务 API
const mainApi = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000
});

// 第三方服务 API
const thirdPartyApi = axios.create({
  baseURL: 'https://api.thirdparty.com',
  timeout: 30000,
  headers: {
    'X-API-Key': 'thirdparty-key'
  }
});

// 文件上传 API(特殊配置)
const uploadApi = axios.create({
  baseURL: 'https://upload.example.com',
  timeout: 60000,
  headers: {
    'Content-Type': 'multipart/form-data'
  }
});

// 使用不同的实例
await mainApi.get('/users');
await thirdPartyApi.get('/weather');
await uploadApi.post('/file', formData);

六、错误处理详解

6.1 错误对象结构

axios.get('/api/notfound')
  .catch(error => {
    if (error.response) {
      // 服务器响应了错误状态码(4xx/5xx)
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      // 请求发送了但没有收到响应
      console.log(error.request);
    } else {
      // 请求配置出错
      console.log(error.message);
    }
    console.log(error.config);
  });

6.2 错误处理最佳实践

// 封装错误处理
class ApiError extends Error {
  constructor(message, status, data) {
    super(message);
    this.status = status;
    this.data = data;
    this.name = 'ApiError';
  }
}

// 全局错误处理函数
function handleApiError(error) {
  if (error.response) {
    const { status, data } = error.response;
    
    switch (status) {
      case 400:
        throw new ApiError('请求参数错误', status, data);
      case 401:
        throw new ApiError('未授权,请重新登录', status, data);
      case 403:
        throw new ApiError('没有权限访问', status, data);
      case 404:
        throw new ApiError('请求资源不存在', status, data);
      case 500:
        throw new ApiError('服务器内部错误', status, data);
      default:
        throw new ApiError(data?.message || '请求失败', status, data);
    }
  } else if (error.request) {
    throw new ApiError('网络异常,请检查网络连接', 0);
  } else {
    throw new ApiError(error.message || '请求配置错误', 0);
  }
}

// 使用
async function fetchUsers() {
  try {
    const response = await axios.get('/api/users');
    return response.data;
  } catch (error) {
    throw handleApiError(error);
  }
}

七、高级特性

7.1 取消请求(CancelToken)

// ===== 方式1:使用 CancelToken.source =====
const source = axios.CancelToken.source();

axios.get('/api/slow-request', {
  cancelToken: source.token
})
.then(response => console.log(response.data))
.catch(error => {
  if (axios.isCancel(error)) {
    console.log('请求已取消:', error.message);
  } else {
    console.error('其他错误:', error);
  }
});

// 取消请求
source.cancel('用户取消了请求');

// ===== 方式2:使用 CancelToken 构造函数 =====
let cancel;
axios.get('/api/slow-request', {
  cancelToken: new axios.CancelToken(c => {
    cancel = c;
  })
});
cancel('用户取消');

// ===== 实战:防抖搜索 =====
let cancelToken;
function search(keyword) {
  // 取消上一次未完成的请求
  if (cancelToken) {
    cancelToken.cancel('取消旧请求');
  }
  
  cancelToken = axios.CancelToken.source();
  
  axios.get('/api/search', {
    params: { q: keyword },
    cancelToken: cancelToken.token
  })
  .then(response => {
    console.log('搜索结果:', response.data);
  })
  .catch(error => {
    if (!axios.isCancel(error)) {
      console.error('搜索失败:', error);
    }
  });
}

7.2 转换器(Transformer)

// ===== 请求转换 =====
axios.defaults.transformRequest = [(data, headers) => {
  // 自动处理不同的数据格式
  if (data instanceof FormData) {
    return data;
  }
  if (data instanceof URLSearchParams) {
    return data.toString();
  }
  // 默认 JSON
  return JSON.stringify(data);
}];

// ===== 响应转换 =====
axios.defaults.transformResponse = [(data) => {
  // 如果数据是字符串,尝试解析为 JSON
  if (typeof data === 'string') {
    try {
      data = JSON.parse(data);
    } catch (e) {
      // 不是 JSON,保持原样
    }
  }
  return data;
}];

7.3 适配器(Adapter)

Axios 支持在不同环境使用不同适配器:

// 浏览器默认使用 XMLHttpRequest
// Node.js 默认使用 http/https

// 自定义适配器(示例)
const customAdapter = (config) => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(config.method, config.url, true);
    xhr.onload = () => resolve({ /* response */ });
    xhr.onerror = reject;
    xhr.send(config.data);
  });
};

// 使用自定义适配器
const instance = axios.create({
  adapter: customAdapter
});

7.4 进度监控

// 上传进度(适合文件上传)
async function uploadFile(file) {
  const formData = new FormData();
  formData.append('file', file);
  
  const response = await axios.post('/api/upload', formData, {
    headers: {
      'Content-Type': 'multipart/form-data'
    },
    onUploadProgress: (progressEvent) => {
      const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
      console.log(`上传进度: ${percent}%`);
      // 更新进度条 UI
      updateProgressBar(percent);
    }
  });
  
  return response.data;
}

// 下载进度(适合大文件下载)
async function downloadLargeFile() {
  const response = await axios.get('/api/large-file', {
    responseType: 'blob',
    onDownloadProgress: (progressEvent) => {
      const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
      console.log(`下载进度: ${percent}%`);
    }
  });
  
  // 触发浏览器下载
  const url = URL.createObjectURL(response.data);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'large-file.pdf';
  a.click();
  URL.revokeObjectURL(url);
}

7.5 请求重试(使用 axios-retry)

npm install axios-retry
import axios from 'axios';
import axiosRetry from 'axios-retry';

// 配置重试
axiosRetry(axios, {
  retries: 3, // 重试次数
  retryDelay: (retryCount) => {
    return retryCount * 1000; // 每次延迟递增 1 秒
  },
  retryCondition: (error) => {
    // 只在特定错误时重试
    return axiosRetry.isNetworkOrIdempotentRequestError(error) ||
           error.response?.status === 503;
  },
  onRetry: (retryCount, error, requestConfig) => {
    console.log(`第 ${retryCount} 次重试`, error.message);
  }
});

// 现在会自动重试
axios.get('/api/unstable-endpoint')
  .then(response => console.log(response.data))
  .catch(error => console.error('所有重试都失败:', error));

八、性能优化与最佳实践

8.1 使用 CancelToken 防止内存泄漏

// React 中清理未完成的请求
useEffect(() => {
  const source = axios.CancelToken.source();
  
  axios.get('/api/data', { cancelToken: source.token })
    .then(res => setData(res.data))
    .catch(err => {
      if (!axios.isCancel(err)) {
        console.error(err);
      }
    });
  
  // 组件卸载时取消所有请求
  return () => source.cancel('组件卸载,取消请求');
}, []);

8.2 配置缓存策略

// 缓存 GET 请求
const cache = new Map();

async function cachedGet(url, params) {
  const key = `${url}_${JSON.stringify(params)}`;
  
  if (cache.has(key)) {
    console.log('使用缓存数据');
    return cache.get(key);
  }
  
  const response = await axios.get(url, { params });
  cache.set(key, response.data);
  return response.data;
}

8.3 请求防抖/节流

import { debounce } from 'lodash';

// 防抖搜索
const searchDebounced = debounce(async (keyword) => {
  const result = await axios.get('/api/search', {
    params: { q: keyword }
  });
  return result.data;
}, 300);

// 使用
input.addEventListener('input', (e) => {
  searchDebounced(e.target.value).then(data => {
    renderResults(data);
  });
});

8.4 使用 HTTP/2 提升性能

// Node.js 中使用 HTTP/2
import http2 from 'http2';

const agent = new http2.Agent();
const instance = axios.create({
  httpsAgent: agent,
  httpAgent: agent
});
Fetch详解 2026-08-24
NodeJs入门 2026-08-24

© 2026 日志记录