Axios 指南

6 阅读8分钟

Axios 指南:现代前端 HTTP 请求库

前言

在现代前端开发中,HTTP 请求是必不可少的功能。Axios 作为一个基于 Promise 的网络请求库,已经成为前端开发者的首选工具。本文将全面介绍 Axios 的使用方法,从基础到高级,帮助你掌握这个强大的 HTTP 客户端库。

一、Axios 简介

1.1 什么是 Axios?

Axios 是一个基于 Promise 的网络请求库,可以同时运行在浏览器和 Node.js 环境中。它是 isomorphic 的(同构的),意味着同一套代码可以在浏览器和 Node.js 中运行。

  • 浏览器端(客户端):使用 XMLHttpRequest
  • Node.js 端(服务端):使用原生 http 模块

1.2 为什么选择 Axios?

相比原生的 fetch API 和 XMLHttpRequest,Axios 具有以下优势:

  1. 基于 Promise:天然支持 Promise,可以使用 async/await
  2. 自动转换 JSON:自动将请求和响应数据转换为 JSON
  3. 请求/响应拦截器:可以在请求发送前和响应接收后进行处理
  4. 取消请求:支持取消正在进行的请求
  5. 超时处理:内置超时处理机制
  6. 错误处理:更好的错误处理机制
  7. 并发请求:支持同时发送多个请求
  8. 请求进度:支持上传和下载进度监控
  9. 防御 XSRF:客户端支持防御跨站请求伪造

二、安装与引入

2.1 安装

使用 npm:

npm install axios

使用 yarn:

yarn add axios

使用 pnpm:

pnpm add axios

使用 CDN(浏览器环境):

<!-- jsDelivr CDN -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

<!-- unpkg CDN -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

2.2 引入方式

ES6 模块(推荐):

import axios from 'axios';

CommonJS:

const axios = require('axios');

浏览器全局变量:

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
  // axios 作为全局变量使用
  axios.get('/api/users')
    .then(response => console.log(response.data));
</script>

三、基本用法

3.1 GET 请求

import axios from 'axios';

// 基本 GET 请求
axios.get('/api/users')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error(error);
  });

// 带参数的 GET 请求
axios.get('/api/users', {
  params: {
    page: 1,
    limit: 10,
    keyword: '张三'
  }
})
  .then(response => {
    console.log(response.data);
  });

// 使用 async/await
async function fetchUsers() {
  try {
    const response = await axios.get('/api/users');
    console.log(response.data);
    return response.data;
  } catch (error) {
    console.error('获取用户失败:', error);
    throw error;
  }
}

3.2 POST 请求

// 基本 POST 请求
axios.post('/api/users', {
  name: '张三',
  email: 'zhangsan@example.com',
  age: 25
})
  .then(response => {
    console.log('创建成功:', response.data);
  })
  .catch(error => {
    console.error('创建失败:', error);
  });

// 使用 async/await
async function createUser(userData) {
  try {
    const response = await axios.post('/api/users', userData);
    return response.data;
  } catch (error) {
    console.error('创建用户失败:', error);
    throw error;
  }
}

// 发送 FormData
const formData = new FormData();
formData.append('name', '张三');
formData.append('avatar', fileInput.files[0]);

axios.post('/api/users', formData, {
  headers: {
    'Content-Type': 'multipart/form-data'
  }
});

3.3 PUT 和 PATCH 请求(开发中常见Get和Post)

// PUT 请求(完整更新)
axios.put('/api/users/1', {
  name: '李四',
  email: 'lisi@example.com',
  age: 30
})
  .then(response => {
    console.log('更新成功:', response.data);
  });

// PATCH 请求(部分更新)
axios.patch('/api/users/1', {
  age: 31
})
  .then(response => {
    console.log('部分更新成功:', response.data);
  });

3.4 DELETE 请求

// DELETE 请求
axios.delete('/api/users/1')
  .then(response => {
    console.log('删除成功');
  })
  .catch(error => {
    console.error('删除失败:', error);
  });

3.5 并发请求

// 使用 Promise.all 并发请求
async function fetchAllData() {
  try {
    const [users, posts, comments] = await Promise.all([
      axios.get('/api/users'),
      axios.get('/api/posts'),
      axios.get('/api/comments')
    ]);
    
    return {
      users: users.data,
      posts: posts.data,
      comments: comments.data
    };
  } catch (error) {
    console.error('获取数据失败:', error);
    throw error;
  }
}

// 使用 axios.all(Promise.all 的别名)
axios.all([
  axios.get('/api/users'),
  axios.get('/api/posts')
])
  .then(axios.spread((usersRes, postsRes) => {
    console.log('用户:', usersRes.data);
    console.log('文章:', postsRes.data);
  }));

四、请求配置

4.1 基本配置

// 使用配置对象发送请求
axios({
  method: 'post',
  url: '/api/users',
  data: {
    name: '张三',
    email: 'zhangsan@example.com'
  },
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token123'
  },
  timeout: 5000, // 超时时间(毫秒)
  params: {
    // URL 参数
    id: 123
  }
});

4.2 常用配置选项

{
  // 请求地址
  url: '/api/users',
  
  // 请求方法(默认 GET)
  method: 'get',
  
  // 请求基础 URL
  baseURL: 'https://api.example.com',
  
  // 请求头
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  },
  
  // URL 参数(仅适用于 GET 请求)
  params: {
    page: 1,
    limit: 10
  },
  
  // 请求体数据(POST、PUT、PATCH)
  data: {
    name: '张三'
  },
  
  // 超时时间(毫秒)
  timeout: 5000,
  
  // 响应数据类型(默认 json)
  responseType: 'json', // 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
  
  // 请求凭证
  withCredentials: false,
  
  // 上传进度
  onUploadProgress: function (progressEvent) {
    const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
    console.log('上传进度:', percentCompleted + '%');
  },
  
  // 下载进度
  onDownloadProgress: function (progressEvent) {
    const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
    console.log('下载进度:', percentCompleted + '%');
  }
}

五、响应结构

5.1 响应对象

axios.get('/api/users')
  .then(response => {
    // 响应对象结构
    console.log(response.data);        // 响应数据
    console.log(response.status);      // HTTP 状态码
    console.log(response.statusText);  // HTTP 状态消息
    console.log(response.headers);    // 响应头
    console.log(response.config);     // 请求配置
  });

5.2 响应数据结构

{
  data: {},           // 服务器返回的数据
  status: 200,        // HTTP 状态码
  statusText: 'OK',   // HTTP 状态消息
  headers: {},       // 响应头
  config: {},         // 请求配置
  request: {}         // 请求对象
}

六、错误处理

6.1 错误对象结构

axios.get('/api/users')
  .catch(error => {
    if (error.response) {
      // 服务器返回了错误状态码 (4xx, 5xx)
      console.error('错误状态码:', error.response.status);
      console.error('错误数据:', error.response.data);
      console.error('错误头:', error.response.headers);
    } else if (error.request) {
      // 请求已发出但没有收到响应
      console.error('请求错误:', error.request);
    } else {
      // 其他错误
      console.error('错误信息:', error.message);
    }
    console.error('错误配置:', error.config);
  });

6.2 错误处理最佳实践

async function fetchData(url) {
  try {
    const response = await axios.get(url);
    return response.data;
  } catch (error) {
    if (error.response) {
      // 服务器返回了错误状态码
      const { status, statusText, data } = error.response;
      
      switch (status) {
        case 400:
          console.error('请求参数错误');
          break;
        case 401:
          console.error('未授权,请重新登录');
          // 可以在这里跳转到登录页
          break;
        case 403:
          console.error('没有权限访问此资源');
          break;
        case 404:
          console.error('请求的资源不存在');
          break;
        case 500:
          console.error('服务器内部错误');
          break;
        default:
          console.error(`请求失败 [${status}]: ${statusText}`);
      }
      
      return Promise.reject({
        status,
        message: data.message || statusText,
        data
      });
    } else if (error.request) {
      // 网络错误
      console.error('网络错误:请求超时或无法连接到服务器');
      return Promise.reject({
        message: '网络错误,请检查网络连接'
      });
    } else {
      // 其他错误
      console.error('请求配置错误:', error.message);
      return Promise.reject({
        message: error.message
      });
    }
  }
}

七、拦截器

7.1 请求拦截器

请求拦截器可以在请求发送前对请求进行处理,常用于添加 token、设置请求头等。

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

7.2 响应拦截器

响应拦截器可以在响应接收后对响应进行处理,常用于统一处理错误、格式化数据等。

// 添加响应拦截器
axios.interceptors.response.use(
  function (response) {
    // 对响应数据做点什么
    
    // 统一处理响应数据格式
    if (response.data.code === 200) {
      return response.data.data;
    } else {
      return Promise.reject(new Error(response.data.message));
    }
  },
  function (error) {
    // 对响应错误做点什么
    
    // 统一处理错误
    if (error.response) {
      const { status } = error.response;
      
      if (status === 401) {
        // 未授权,清除 token 并跳转到登录页
        localStorage.removeItem('token');
        window.location.href = '/login';
      } else if (status === 403) {
        // 没有权限
        console.error('没有权限访问此资源');
      } else if (status >= 500) {
        // 服务器错误
        console.error('服务器错误,请稍后重试');
      }
    }
    
    return Promise.reject(error);
  }
);

7.3 移除拦截器

// 创建拦截器
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});

// 移除拦截器
axios.interceptors.request.eject(myInterceptor);

八、Axios 实例

8.1 创建实例

当需要为不同的 API 配置不同的 baseURL、timeout 等时,可以创建多个 axios 实例。

import axios from 'axios';

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

// 使用实例
apiClient.get('/users')
  .then(response => {
    console.log(response.data);
  });

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

8.2 多个实例示例

// API 客户端
const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000
});

// 文件上传客户端
const uploadClient = axios.create({
  baseURL: 'https://upload.example.com',
  timeout: 30000,
  headers: {
    'Content-Type': 'multipart/form-data'
  }
});

// 第三方 API 客户端
const thirdPartyClient = axios.create({
  baseURL: 'https://third-party-api.com',
  timeout: 5000
});

九、取消请求

9.1 使用 AbortController(推荐)

// 创建 AbortController
const controller = new AbortController();

// 发送请求
axios.get('/api/users', {
  signal: controller.signal
})
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    if (axios.isCancel(error)) {
      console.log('请求已取消:', error.message);
    } else {
      console.error('请求失败:', error);
    }
  });

// 取消请求
controller.abort();

9.2 在组件中使用

// Vue 组件示例
export default {
  data() {
    return {
      controller: null
    };
  },
  methods: {
    async fetchData() {
      // 如果已有请求在进行,先取消
      if (this.controller) {
        this.controller.abort();
      }
      
      // 创建新的控制器
      this.controller = new AbortController();
      
      try {
        const response = await axios.get('/api/users', {
          signal: this.controller.signal
        });
        return response.data;
      } catch (error) {
        if (axios.isCancel(error)) {
          console.log('请求已取消');
        } else {
          console.error('请求失败:', error);
        }
      }
    }
  },
  beforeUnmount() {
    // 组件卸载时取消请求
    if (this.controller) {
      this.controller.abort();
    }
  }
};

十、默认配置

10.1 全局默认配置

// 设置全局默认配置
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.timeout = 5000;
axios.defaults.headers.common['Authorization'] = 'Bearer token';
axios.defaults.headers.post['Content-Type'] = 'application/json';

10.2 实例默认配置

const instance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000
});

// 修改实例默认配置
instance.defaults.headers.common['Authorization'] = 'Bearer new-token';

10.3 配置优先级

配置的优先级从高到低:

  1. 请求配置(config 参数)
  2. 实例默认配置(instance.defaults
  3. 全局默认配置(axios.defaults

十一、实际应用示例

11.1 封装 Axios 工具类

// utils/request.js
import axios from 'axios';
import { message } from 'ant-design-vue'; // 或其他 UI 库的消息提示

// 创建 axios 实例
const service = axios.create({
  baseURL: process.env.VUE_APP_API_BASE_URL,//从环境变量中获取
  timeout: 10000
});

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 添加 token
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  error => {
    console.error('请求错误:', error);
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data;
    
    // 根据后端返回的数据结构进行处理
    if (res.code !== 200) {
      message.error(res.message || '请求失败');
      
      // 特殊状态码处理
      if (res.code === 401) {
        // 未授权,跳转到登录页
        localStorage.removeItem('token');
        window.location.href = '/login';
      }
      
      return Promise.reject(new Error(res.message || '请求失败'));
    } else {
      return res.data;
    }
  },
  error => {
    console.error('响应错误:', error);
    
    let errorMessage = '请求失败';
    if (error.response) {
      const { status, data } = error.response;
      errorMessage = data.message || `请求失败 [${status}]`;
      
      if (status === 401) {
        localStorage.removeItem('token');
        window.location.href = '/login';
      }
    } else if (error.request) {
      errorMessage = '网络错误,请检查网络连接';
    }
    
    message.error(errorMessage);
    return Promise.reject(error);
  }
);

export default service;

11.2 API 接口封装(demo)

// api/user.js
import request from '@/utils/request';

// 获取用户列表
export function getUserList(params) {
  return request({
    url: '/users',
    method: 'get',
    params
  });
}

// 获取用户详情
export function getUserDetail(id) {
  return request({
    url: `/users/${id}`,
    method: 'get'
  });
}

// 创建用户
export function createUser(data) {
  return request({
    url: '/users',
    method: 'post',
    data
  });
}

// 更新用户
export function updateUser(id, data) {
  return request({
    url: `/users/${id}`,
    method: 'put',
    data
  });
}

// 删除用户
export function deleteUser(id) {
  return request({
    url: `/users/${id}`,
    method: 'delete'
  });
}

11.3 在 Vue 组件中使用

// Vue 组件
import { getUserList, createUser } from '@/api/user';

export default {
  data() {
    return {
      users: [],
      loading: false
    };
  },
  async mounted() {
    await this.loadUsers();
  },
  methods: {
    async loadUsers() {
      this.loading = true;
      try {
        this.users = await getUserList({
          page: 1,
          limit: 10
        });
      } catch (error) {
        console.error('加载用户失败:', error);
      } finally {
        this.loading = false;
      }
    },
    
    async handleCreateUser(userData) {
      try {
        await createUser(userData);
        this.$message.success('创建成功');
        await this.loadUsers(); // 重新加载列表
      } catch (error) {
        console.error('创建用户失败:', error);
      }
    }
  }
};

11.4 文件上传示例

// 上传单个文件
async function uploadFile(file) {
  const formData = new FormData();
  formData.append('file', file);
  
  try {
    const response = await axios.post('/api/upload', formData, {
      headers: {
        'Content-Type': 'multipart/form-data'
      },
      onUploadProgress: (progressEvent) => {
        const percentCompleted = Math.round(
          (progressEvent.loaded * 100) / progressEvent.total
        );
        console.log('上传进度:', percentCompleted + '%');
      }
    });
    
    return response.data;
  } catch (error) {
    console.error('上传失败:', error);
    throw error;
  }
}

// 上传多个文件
async function uploadFiles(files) {
  const formData = new FormData();
  files.forEach((file, index) => {
    formData.append(`files[${index}]`, file);
  });
  
  return axios.post('/api/upload/multiple', formData, {
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  });
}

十二、常见问题与最佳实践

12.1 常见问题

1. 如何处理 CORS 跨域问题?

// 后端需要设置 CORS 头
// 前端可以设置 withCredentials
在vite中找到vite.config.js配置server.proxy转发代理请求

2. 如何防止重复请求?

let pendingRequests = new Map();

function addPendingRequest(config) {
  const requestKey = `${config.method}-${config.url}`;
  config.cancelToken = config.cancelToken || axios.CancelToken.source();
  if (pendingRequests.has(requestKey)) {
    const cancel = pendingRequests.get(requestKey);
    delete pendingRequests[requestKey];
    cancel.cancel('重复请求已取消');
  }
  pendingRequests.set(requestKey, config.cancelToken);
}

function removePendingRequest(config) {
  const requestKey = `${config.method}-${config.url}`;
  if (pendingRequests.has(requestKey)) {
    pendingRequests.delete(requestKey);
  }
}

3. 如何实现请求重试?

async function requestWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      if (i === maxRetries - 1) {
        throw error;
      }
      // 等待后重试
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

12.2 最佳实践

  1. 统一封装:创建统一的 axios 实例,配置 baseURL、timeout 等
  2. 使用拦截器:统一处理 token、错误等
  3. 错误处理:统一错误处理逻辑,提供友好的错误提示
  4. 类型安全:使用 TypeScript 定义请求和响应的类型
  5. 取消请求:在组件卸载时取消未完成的请求
  6. 请求缓存:对不经常变化的数据进行缓存
  7. 加载状态:使用拦截器统一管理加载状态

十三、总结

Axios 是一个功能强大、易于使用的 HTTP 客户端库,它提供了:

  • ✅ 基于 Promise 的 API,支持 async/await
  • ✅ 请求和响应拦截器
  • ✅ 自动转换 JSON 数据
  • ✅ 取消请求功能
  • ✅ 超时处理
  • ✅ 错误处理机制
  • ✅ 支持浏览器和 Node.js

通过本文的学习,你应该能够:

  1. 理解 Axios 的基本概念和特性
  2. 掌握 Axios 的基本用法和高级特性
  3. 学会使用拦截器处理请求和响应
  4. 能够封装自己的 Axios 工具类
  5. 了解最佳实践和常见问题的解决方案

希望这篇文章能帮助你更好地使用 Axios 进行前端开发!

参考资源


如果这篇文章对你有帮助,欢迎点赞、收藏和分享!如有问题,欢迎在评论区留言讨论。