快速上手 Axios:前端异步请求库入门教程
在现代前端开发中,与后端进行数据交互是不可或缺的一部分。无论是获取用户列表、提交表单数据,还是上传文件,都需要通过网络请求来完成。虽然浏览器原生提供了 XMLHttpRequest
和 fetch
API 来处理这些任务,但一个功能强大、易于使用的第三方库往往能极大地提升开发效率和体验。
Axios 正是这样一个备受青睐的前端异步请求库。它基于 Promise,可以在浏览器和 Node.js 环境中使用,提供了统一的 API,并且拥有许多原生方法所不具备的便利特性,例如请求/响应拦截器、请求取消、自动转换 JSON 数据等。
本篇文章将带你由浅入深地学习 Axios 的使用。我们将从基础的安装和发送请求开始,逐步深入到请求配置、错误处理、async/await
结合、创建实例、请求/响应拦截器等高级用法。读完本文,你将能够熟练地在你的前端项目中运用 Axios 进行数据请求。
目录
-
为什么选择 Axios?
- 告别
XMLHttpRequest
的繁琐 - 比
fetch
更方便的特性 - Axios 的核心优势
- 告别
-
安装和引入
- 使用 npm 或 yarn
- 使用 CDN
-
你的第一个 Axios 请求:GET
- 基础用法:Promise 风格
- 理解 Response 对象
- 发送带参数的 GET 请求
-
发送数据:POST 请求
- 基础用法
- 发送不同格式的数据
-
其他 HTTP 方法
- PUT、DELETE、PATCH 等
-
配置请求
- 通过配置对象统一管理
- 常用配置选项详解 (
headers
,timeout
,params
,data
等)
-
深入理解错误处理
- Promise 的
.catch()
- 错误对象结构
- 不同类型的错误及判断
- 统一错误处理策略
- Promise 的
-
拥抱
async/await
- 用
async/await
简化异步代码 - 结合
try...catch
进行错误处理
- 用
-
创建 Axios 实例
- 为什么需要实例?
- 创建和使用实例
- 为不同 API 设置默认配置
-
请求和响应拦截器 (Interceptors)
- 拦截器是什么?
- 请求拦截器的应用场景 (e.g., 添加 Token)
- 响应拦截器的应用场景 (e.g., 统一错误码处理, 数据预处理)
- 添加和移除拦截器
-
取消请求
- 为什么需要取消请求?
- 使用
CancelToken
(旧方法) - 使用
AbortController
(新方法)
-
发送多个并发请求
- 使用
axios.all
和axios.spread
- 使用
Promise.all
- 使用
-
总结与进阶
1. 为什么选择 Axios?
在 Axios 出现之前,前端进行异步请求主要依赖于原生的 XMLHttpRequest
(XHR) 对象。虽然 XHR 功能强大,但其基于事件回调的异步模型写起来比较繁琐,容易陷入“回调地狱”,且 API 设计不够直观。
随着 Promise 的普及,浏览器引入了 fetch
API,它提供了更现代、更简洁的 API 设计,并且原生支持 Promise,写异步代码更加方便。
那么,为什么我们还需要 Axios 呢?
- 跨平台: Axios 既可以在浏览器中使用,也可以在 Node.js 环境中运行(例如在服务器端渲染或构建工具中)。
fetch
API 主要面向浏览器环境。 - 统一的 API: 在浏览器和 Node.js 中,Axios 提供了完全一致的使用体验。
- Promise 支持: 这是与旧的 XHR 相比的巨大优势,使得异步代码更加整洁易读。
fetch
也支持 Promise。 - 拦截器: 这是 Axios 最强大的特性之一。你可以在请求发送前或响应返回后进行拦截处理,例如统一添加请求头、身份认证信息、请求日志、统一错误处理等。这是
fetch
API 原生不具备的。 - 自动转换 JSON 数据: Axios 会自动将响应的 JSON 数据解析成 JavaScript 对象,发送请求时如果发送的是 JavaScript 对象,它也会自动将其序列化为 JSON 字符串(默认情况下),并设置合适的
Content-Type
头。fetch
需要手动调用.json()
方法进行解析。 - 请求取消: 可以在请求发出后取消它,这对于优化用户体验、避免资源浪费非常有用(例如用户在请求完成前切换了页面)。
fetch
需要结合AbortController
。 - 客户端支持防止 CSRF/XSRF: Axios 提供了一些内置的支持来防止跨站请求伪造。
- 丰富的配置选项: 可以轻松地设置请求超时时间、baseURL、请求头、请求体、处理二进制数据等。
- 更好的错误处理: Axios 在接收到 HTTP 状态码非 2xx 时,会默认将其视为错误并抛出异常,可以直接在
.catch()
中处理。而fetch
只有在网络错误时才会抛出异常,对于 4xx 或 5xx 的 HTTP 状态码,fetch
仍然会将它们视为成功的响应,需要手动检查response.ok
或response.status
。
总而言之,Axios 提供了比原生 XMLHttpRequest
更简洁的 API,并且在许多方面比 fetch
提供了更方便、更强大的功能,尤其是在拦截器和错误处理方面,这使得 Axios 成为构建复杂前端应用时进行网络请求的首选库。
2. 安装和引入
使用 Axios 非常简单,可以通过包管理器安装,也可以通过 CDN 直接引入。
使用 npm 或 yarn (推荐)
如果你使用现代前端构建工具 (如 Webpack, Vite, Parcel),推荐使用 npm 或 yarn 进行安装:
“`bash
使用 npm
npm install axios
或者使用 yarn
yarn add axios
“`
安装完成后,在你的 JavaScript 文件中通过 import
语句引入:
javascript
import axios from 'axios';
或者如果你使用 CommonJS 模块系统 (例如在 Node.js 环境或旧的构建流程中):
javascript
const axios = require('axios');
使用 CDN
如果你只是在一个简单的 HTML 文件中快速使用 Axios,或者不使用构建工具,可以通过 CDN 链接直接在 script
标签中引入:
“`html
“`
通过 CDN 引入后,Axios 对象会暴露在全局的 window
对象下,你可以直接通过 window.axios
或 axios
来使用它。
“`html
Axios 快速上手
“`
在后续的示例中,我们将主要使用 ES Module 的 import
语法。
3. 你的第一个 Axios 请求:GET
最常见的网络请求是获取数据,这通常使用 HTTP 的 GET 方法。Axios 提供了简洁的方法来发送 GET 请求。
基础用法:Promise 风格
Axios 的所有请求方法都返回一个 Promise 对象,这意味着你可以使用 .then()
来处理成功响应,使用 .catch()
来处理错误。
“`javascript
import axios from ‘axios’;
// 假设我们要从这个 URL 获取数据
const url = ‘https://jsonplaceholder.typicode.com/posts/1’;
axios.get(url)
.then(function (response) {
// 请求成功,response 对象包含了响应的详细信息
console.log(“请求成功!”);
console.log(“状态码:”, response.status); // HTTP 状态码,例如 200, 404, 500
console.log(“状态文本:”, response.statusText); // 状态文本,例如 OK, Not Found
console.log(“响应头:”, response.headers); // 响应头信息
console.log(“响应数据:”, response.data); // 后端返回的实际数据,通常是 JSON 对象或数组
console.log(“请求配置:”, response.config); // 发送请求时使用的配置
console.log(“XMLHttpRequest 对象 (仅浏览器):”, response.request); // 底层的 XMLHttpRequest 对象或 http.ClientRequest 对象
// 你通常只关心 response.data
const post = response.data;
console.log("获取到的文章标题:", post.title);
})
.catch(function (error) {
// 请求失败,error 对象包含了错误信息
console.error("请求失败!");
console.error(error);
// 后面会详细讲解如何处理不同类型的错误
})
.finally(function () {
// 无论成功或失败,都会执行的代码 (可选)
console.log("请求结束。");
});
“`
理解 Response 对象
当 Axios 请求成功时,.then()
回调函数会接收到一个 response
对象。这个对象包含了以下核心属性:
data
: 后端返回的实际数据。如果后端返回的是 JSON 字符串,Axios 会自动解析成 JavaScript 对象或数组。status
: HTTP 状态码(例如 200, 404, 500)。statusText
: HTTP 状态文本(例如 ‘OK’, ‘Not Found’)。headers
: 响应头信息,以 JavaScript 对象形式表示,属性名都是小写。config
: 发送请求时使用的配置对象。request
: 原始的XMLHttpRequest
对象(在浏览器中)或http.ClientRequest
对象(在 Node.js 中)。
在大多数情况下,你最常使用的是 response.data
来获取后端返回的实际数据。
发送带参数的 GET 请求
GET 请求通常通过 URL 的查询字符串(Query String)向后端传递参数,例如 /api/users?id=1&name=test
。Axios 使得发送带参数的 GET 请求变得非常简单,你只需要在请求配置中提供一个 params
对象。
“`javascript
import axios from ‘axios’;
const url = ‘https://jsonplaceholder.typicode.com/posts’; // 获取文章列表的接口
// 定义要发送的参数
const params = {
userId: 1, // 获取用户 ID 为 1 的所有文章
_limit: 5 // 限制返回的文章数量为 5 (这个 API 支持的过滤参数)
};
axios.get(url, {
params: params // 在配置对象中使用 params 属性
})
.then(function (response) {
console.log(“获取用户文章列表成功!”);
console.log(“获取到文章数量:”, response.data.length);
console.log(“第一篇文章:”, response.data[0]);
})
.catch(function (error) {
console.error(“获取用户文章列表失败!”, error);
});
// Axios 会自动将上述请求转换为类似这样的 URL:
// https://jsonplaceholder.typicode.com/posts?userId=1&_limit=5
“`
将参数放在 params
对象中交给 Axios 处理,它会自动帮你拼接成正确的查询字符串,并且会正确地进行 URL 编码。这比手动拼接字符串要方便和安全得多。
4. 发送数据:POST 请求
POST 请求通常用于创建新资源、提交表单数据等,需要将数据放在请求体(Request Body)中发送给服务器。
Axios 的 POST 方法接收两个主要参数:URL 和要发送的数据。
“`javascript
import axios from ‘axios’;
const url = ‘https://jsonplaceholder.typicode.com/posts’; // 创建新文章的接口
// 定义要发送的数据,通常是一个 JavaScript 对象
const newPost = {
title: ‘Axios 使用教程’,
body: ‘这是一篇关于 Axios 入门使用的详细教程内容…’,
userId: 1
};
axios.post(url, newPost) // 第一个参数是 URL,第二个参数是数据
.then(function (response) {
console.log(“创建文章成功!”);
console.log(“新文章的数据:”, response.data); // 通常后端会返回创建成功的资源及 ID
console.log(“状态码:”, response.status); // 状态码通常是 201 (Created)
})
.catch(function (error) {
console.error(“创建文章失败!”, error);
});
“`
当发送的数据是 JavaScript 对象时,Axios 默认会将 Content-Type
设置为 application/json
,并将对象序列化为 JSON 字符串发送。
发送不同格式的数据
除了 JSON,有时你可能需要发送其他格式的数据,例如 application/x-www-form-urlencoded
(传统的表单提交格式)或 multipart/form-data
(用于文件上传)。
-
application/x-www-form-urlencoded
: 你需要将数据转换成 URLSearchParams 对象或使用第三方库 (如qs
) 来序列化数据,并设置Content-Type
头。“`javascript
import axios from ‘axios’;
import qs from ‘qs’; // 可能需要安装 npm install qsconst url = ‘/api/submitForm’; // 假设这是一个接收表单数据的接口
const formData = {
name: ‘张三’,
age: 30,
email: ‘[email protected]’
};axios.post(url, qs.stringify(formData), {
headers: {
‘Content-Type’: ‘application/x-www-form-urlencoded’
}
})
.then(response => {
console.log(‘表单提交成功’, response.data);
})
.catch(error => {
console.error(‘表单提交失败’, error);
});// 或者使用原生的 URLSearchParams (现代浏览器和 Node.js 支持)
const params = new URLSearchParams();
params.append(‘name’, ‘张三’);
params.append(‘age’, ’30’);
params.append(’email’, ‘[email protected]’);axios.post(url, params) // Axios 识别 URLSearchParams 对象,并自动设置 Content-Type 为 application/x-www-form-urlencoded
.then(response => {
console.log(‘表单提交成功 (URLSearchParams)’, response.data);
})
.catch(error => {
console.error(‘表单提交失败 (URLSearchParams)’, error);
});
“` -
multipart/form-data
: 主要用于文件上传。你需要使用FormData
对象。“`javascript
import axios from ‘axios’;const url = ‘/api/uploadFile’; // 文件上传接口
const formData = new FormData();
const fileInput = document.querySelector(‘input[type=”file”]’); // 获取文件输入的 DOM 元素if (fileInput.files && fileInput.files[0]) {
formData.append(‘myFile’, fileInput.files[0]); // ‘myFile’ 是后端接收文件的字段名
formData.append(‘userName’, ‘tester’); // 也可以添加其他字段
}axios.post(url, formData, {
headers: {
‘Content-Type’: ‘multipart/form-data’ // 当使用 FormData 时,Axios 会自动设置这个头
// 实际上,当你发送 FormData 对象时,Axios 会自动设置正确的 Content-Type 头,通常你无需手动设置
}
})
.then(response => {
console.log(‘文件上传成功’, response.data);
})
.catch(error => {
console.error(‘文件上传失败’, error);
});
``
FormData
注意:当发送对象时,你通常不需要手动设置
Content-Type头,浏览器会自动生成一个包含
boundary信息的
Content-Type`。
5. 其他 HTTP 方法
除了 GET 和 POST,HTTP 协议还定义了其他常用的方法,如 PUT、DELETE、PATCH 等。Axios 也为这些方法提供了相应的方法:
axios.put(url, data[, config])
: 通常用于更新整个资源。axios.delete(url[, config])
: 通常用于删除资源。axios.patch(url, data[, config])
: 通常用于部分更新资源。axios.head(url[, config])
: 获取资源的头部信息,不返回响应体。axios.options(url[, config])
: 获取资源支持的通信选项。
这些方法的用法与 axios.post
类似,put
和 patch
方法也需要传递数据,而 delete
和 head
方法通常不需要直接传递数据(如果需要传递参数,可以使用 config
对象的 params
或在某些情况下使用 data
,但 RESTful API 设计中 delete 通常只通过 URL 或 params 标识资源)。
PUT 示例 (更新资源):
“`javascript
import axios from ‘axios’;
const url = ‘https://jsonplaceholder.typicode.com/posts/1’; // 更新 ID 为 1 的文章
const updatedPost = {
id: 1, // 有些 API 要求 PUT 时包含 ID
title: ‘更新后的标题’,
body: ‘这是更新后的文章内容…’,
userId: 1
};
axios.put(url, updatedPost)
.then(response => {
console.log(‘文章更新成功’, response.data);
})
.catch(error => {
console.error(‘文章更新失败’, error);
});
“`
DELETE 示例 (删除资源):
“`javascript
import axios from ‘axios’;
const url = ‘https://jsonplaceholder.typicode.com/posts/1’; // 删除 ID 为 1 的文章
axios.delete(url)
.then(response => {
console.log(‘文章删除成功’, response.data); // 删除成功可能返回空数据或表示成功的状态
console.log(‘状态码:’, response.status); // 通常是 200 (OK) 或 204 (No Content)
})
.catch(error => {
console.error(‘文章删除失败’, error);
});
“`
6. 配置请求
除了直接使用 axios.get(url, { params: ... })
或 axios.post(url, data, { headers: ... })
这种方式传递配置,Axios 允许你将所有请求相关的配置集中在一个配置对象中,然后作为第二个参数(对于不需要 data 的方法,如 GET/DELETE)或第三个参数(对于需要 data 的方法,如 POST/PUT/PATCH)传递。
“`javascript
import axios from ‘axios’;
const config = {
method: ‘get’, // 请求方法
url: ‘https://jsonplaceholder.typicode.com/posts’, // 请求 URL
params: { // GET 请求参数
userId: 1,
_limit: 3
},
headers: { // 请求头
‘X-Custom-Header’: ‘foobar’,
‘Authorization’: ‘Bearer your_token_here’
},
timeout: 5000, // 请求超时时间 (毫秒)
responseType: ‘json’ // 期望的响应数据类型,可选值:’arraybuffer’, ‘blob’, ‘document’, ‘json’, ‘text’, ‘stream’
// 对于 POST/PUT/PATCH 等方法,可以使用 data 属性发送请求体数据
// data: {
// title: ‘新标题’,
// body: ‘新内容’
// }
};
axios(config) // 直接将配置对象传递给 axios 函数
.then(response => {
console.log(‘通过配置对象发送请求成功’, response.data);
})
.catch(error => {
console.error(‘通过配置对象发送请求失败’, error);
});
“`
Axios 的所有请求方法 (axios.get
, axios.post
等) 实际上都是 axios(config)
的语法糖,它们内部会构造一个合适的配置对象并传递给核心的 axios
函数。
常用配置选项详解:
url
: 请求的服务器 URL。method
: 请求方法,如'get'
,'post'
,'put'
,'delete'
等。默认是'get'
。baseURL
: 将自动加在url
前面,除非url
是一个绝对 URL。设置baseURL
可以简化请求 URL 的书写。headers
: 自定义请求头。可以是一个对象,例如{ 'X-Requested-With': 'XMLHttpRequest' }
。params
: 将被作为 URL 查询参数拼接到 URL 上。只适用于 GET, HEAD 方法,会附加在 URL 后面。data
: 将被作为请求体发送的数据。只适用于 POST, PUT, PATCH 等方法。timeout
: 请求超时时间,单位是毫秒 (ms)。如果在超时时间内没有收到响应,请求将被取消。responseType
: 期望从服务器接收到的响应数据类型。默认是'json'
。withCredentials
: 是否发送 cross-site access control 请求时,带上 cookie 和 Authorization 头部。auth
: 用于 HTTP Basic Authentication 的认证凭据{ username: '...', password: '...' }
。proxy
: 设置代理服务器。cancelToken
: 用于取消请求。signal
: 用于取消请求 (基于AbortController
)。
7. 深入理解错误处理
在实际开发中,网络请求失败是常有的事情,可能是网络问题、服务器错误、客户端参数错误等等。正确地处理这些错误对于应用的健壮性和用户体验至关重要。
Axios 的 Promise 特性使得错误处理非常方便,失败的请求会进入 .catch()
回调。
“`javascript
axios.get(‘/api/nonexistent-endpoint’) // 访问一个不存在的接口 (会返回 404)
.then(response => {
console.log(‘请求成功 (理论上不会执行)’, response);
})
.catch(error => {
console.error(‘请求失败!’, error);
// error 对象结构分析
if (error.response) {
// 请求已发出,但服务器响应的状态码不在 2xx 范围内
console.log('------ error.response ------');
console.log('错误数据:', error.response.data); // 服务器返回的错误信息
console.log('状态码:', error.response.status); // 错误状态码,例如 404, 500
console.log('响应头:', error.response.headers);
} else if (error.request) {
// 请求已发出,但没有收到任何响应
// 例如:浏览器端请求没有跨域权限;服务器端请求没有响应等
console.log('------ error.request ------');
console.log('错误请求:', error.request);
console.log('错误信息:', error.message); // 可能是 'Network Error' 等
} else {
// 在发送请求时发生了一些事情,触发了错误
console.log('------ other error ------');
console.log('错误信息:', error.message); // 例如配置错误等
}
console.log('请求配置:', error.config); // 错误请求的配置信息
});
“`
错误对象结构
当 Axios 捕获到错误时,会传入一个 error
对象到 .catch()
回调中。这个对象包含以下重要的属性:
error.response
: 如果请求已发送并且服务器返回了非 2xx 的状态码(例如 404, 500),则此对象存在,包含data
,status
,headers
等属性,与成功响应时的response
对象类似。error.request
: 如果请求已发出但没有收到响应(例如网络错误、跨域问题),则此对象存在,它是浏览器中的XMLHttpRequest
实例或 Node.js 中的http.ClientRequest
实例。error.message
: 错误的文本信息,例如"Request failed with status code 404"
或"Network Error"
。error.config
: 生成错误时使用的请求配置对象。error.code
: 可选的错误码,例如'ECONNABORTED'
表示请求超时。
不同类型的错误及判断
理解错误对象的结构,可以帮助我们区分不同类型的错误并进行针对性处理:
- HTTP 状态码错误 (例如 404, 500):
error.response
存在。这是最常见的后端接口错误。你可以根据error.response.status
来判断具体的错误类型,例如 401 未授权、403 禁止访问、404 资源不存在、500 服务器内部错误等,并给出相应的用户提示。 - 网络错误 (例如断网, 跨域问题):
error.request
存在且error.response
不存在。此时通常error.message
是"Network Error"
。 - 请求超时错误:
error.code
为'ECONNABORTED'
。 - 请求取消错误: 后面会讲到,如果请求被手动取消,会进入
.catch()
,可以通过axios.isCancel(error)
来判断。 - 其他错误: 例如请求配置有问题等,此时可能只有
error.message
。
统一错误处理策略
在大型应用中,通常会有一个全局的错误处理机制。例如,当收到 401 错误时,统一跳转到登录页;当收到 500 错误时,给用户一个通用的错误提示;当网络错误时,提示用户检查网络连接。这可以通过 响应拦截器 来实现(后面会详细讲解)。
8. 拥抱 async/await
随着 ES2017 的普及,async/await
语法成为处理 Promise 的更优雅方式。由于 Axios 方法返回 Promise,因此可以完美地与 async/await
结合使用,让异步代码看起来像同步代码一样简洁。
“`javascript
import axios from ‘axios’;
async function fetchPost(postId) {
try {
const url = https://jsonplaceholder.typicode.com/posts/${postId}
;
const response = await axios.get(url); // 使用 await 等待 Promise 解决
// 如果 await 成功,response 就是 Promise resolved 的值 (Axios 的 response 对象)
console.log(`获取文章 ${postId} 成功!`);
console.log("文章数据:", response.data);
return response.data; // 返回实际数据
} catch (error) {
// 如果 await 过程中发生错误 (Promise reject),则会捕获到 error
console.error(`获取文章 ${postId} 失败!`);
console.error(error);
// 可以根据错误类型做不同处理
if (axios.isCancel(error)) {
console.log('请求被取消:', error.message);
} else if (error.response) {
console.error('状态码:', error.response.status);
console.error('错误详情:', error.response.data);
} else if (error.request) {
console.error('网络或无响应错误:', error.message);
} else {
console.error('其他错误:', error.message);
}
// 可以选择抛出错误让上层调用者处理
throw error;
} finally {
console.log(`fetchPost(${postId}) 函数执行结束。`);
}
}
// 调用 async 函数
fetchPost(1);
fetchPost(999); // 演示一个可能失败的请求
“`
使用 async/await
的优点:
- 代码更简洁: 避免了
.then().catch()
的链式调用,使代码逻辑更线性。 - 更易读: 异步流程看起来更像同步流程,更容易理解。
- 错误处理更直观: 可以使用同步代码中常见的
try...catch
块来捕获错误。
这是现代 JavaScript 中处理异步操作的主流方式,强烈推荐在 Axios 请求中使用 async/await
。
9. 创建 Axios 实例
在大型应用中,你可能会向多个不同的后端 API 发送请求,或者同一个 API 在不同环境 (开发/测试/生产) 有不同的 baseURL
和默认配置。为每次请求都重复设置这些配置会非常繁琐且容易出错。
Axios 提供了 axios.create()
方法,允许你创建具有自定义默认配置的新 Axios 实例。
“`javascript
import axios from ‘axios’;
// 创建一个用于处理用户相关接口的实例
const userApi = axios.create({
baseURL: ‘https://api.example.com/user’, // 用户接口的基础 URL
timeout: 10000, // 用户接口超时时间设置为 10 秒
headers: {
‘X-Custom-User-Header’: ‘user-service’
}
});
// 创建一个用于处理商品相关接口的实例
const productApi = axios.create({
baseURL: ‘https://api.example.com/product’, // 商品接口的基础 URL
timeout: 5000, // 商品接口超时时间设置为 5 秒
headers: {
‘X-Custom-Product-Header’: ‘product-service’
}
});
// 现在可以使用这些实例来发送请求,它们会自动应用默认配置
async function getUserInfo(userId) {
try {
// 请求会发送到 https://api.example.com/user/info/123
const response = await userApi.get(/info/${userId}
);
console.log(‘获取用户信息成功:’, response.data);
return response.data;
} catch (error) {
console.error(‘获取用户信息失败:’, error);
throw error;
}
}
async function getProductDetails(productId) {
try {
// 请求会发送到 https://api.example.com/product/details?id=abc
const response = await productApi.get(‘/details’, {
params: { id: productId }
});
console.log(‘获取商品详情成功:’, response.data);
return response.data;
} catch (error) {
console.error(‘获取商品详情失败:’, error);
throw error;
}
}
getUserInfo(123);
getProductDetails(‘abc’);
// 你仍然可以使用全局的 axios 对象发送请求到其他地方
axios.get(‘https://jsonplaceholder.typicode.com/posts/1’)
.then(…)
.catch(…);
“`
创建实例的好处:
- 代码复用: 避免重复书写相同的
baseURL
、headers
、timeout
等配置。 - 配置隔离: 不同实例之间的配置是独立的,互不影响。
- 拦截器隔离: 最重要的是,你可以为不同的实例配置不同的拦截器,实现更精细的控制(例如,只有需要登录的接口实例才添加 Token 拦截器)。
10. 请求和响应拦截器 (Interceptors)
拦截器是 Axios 最强大和实用的特性之一。它允许你在请求被发送到服务器之前,或响应被 .then()
或 .catch()
处理之前,对请求或响应进行拦截和修改。
拦截器分为两种:请求拦截器和响应拦截器。
请求拦截器 (Request Interceptors)
在请求发送到服务器之前被调用。常见的应用场景:
- 在请求头中统一添加身份认证 Token。
- 统一添加一些通用的请求参数 (如版本号、设备信息)。
- 显示全局加载动画。
- 对请求数据进行加密或格式转换。
“`javascript
import axios from ‘axios’;
// 添加请求拦截器
// axios.interceptors.request.use(fulfilled, rejected);
// fulfilled: 请求发送前的处理函数
// rejected: 请求错误时的处理函数 (基本不会用到,因为请求还没发出去,哪来的请求错误?)
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么,例如添加 Token
console.log(‘>>> 请求拦截器:准备发送请求…’);
const token = localStorage.getItem('authToken'); // 假设 Token 存储在 localStorage
if (token) {
// 如果 Token 存在,则在请求头中添加 Authorization 字段
// 注意:config.headers 是一个对象,直接修改它即可
config.headers['Authorization'] = 'Bearer ' + token;
console.log('>>> 请求拦截器:添加 Authorization 头');
}
// 务必返回 config 对象,否则请求将无法继续
return config;
}, function (error) {
// 对请求错误做些什么
console.error(‘>>> 请求拦截器:请求配置错误!’, error);
// 也可以选择在这里处理错误,或者直接 Promise.reject
return Promise.reject(error);
});
// 使用了上述拦截器的请求示例
axios.get(‘/api/needs-auth’)
.then(response => {
console.log(‘接口 /api/needs-auth 请求成功’, response.data);
})
.catch(error => {
console.error(‘接口 /api/needs-auth 请求失败’, error);
});
“`
响应拦截器 (Response Interceptors)
在服务器返回响应后,但在 .then()
或 .catch()
处理之前被调用。常见的应用场景:
- 统一处理后端返回的业务状态码 (例如,如果返回特定错误码表示 Token 过期,则跳转到登录页)。
- 统一处理 HTTP 状态码非 2xx 的情况。
- 对响应数据进行预处理或格式转换。
- 隐藏全局加载动画。
- 统一处理接口返回的错误信息并显示给用户。
“`javascript
import axios from ‘axios’;
// 添加响应拦截器
// axios.interceptors.response.use(fulfilled, rejected);
// fulfilled: 响应成功时的处理函数 (状态码在 2xx 范围内)
// rejected: 响应失败时的处理函数 (状态码不在 2xx 范围内 或 请求没有响应/超时)
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么 (例如,只返回 response.data)
console.log(‘<<< 响应拦截器:收到成功响应!’);
console.log(‘<<< 响应拦截器:状态码’, response.status);
// 你可以在这里对数据进行预处理,例如检查后端自定义的状态码
// if (response.data && response.data.code !== 0) {
// // 后端业务错误,抛出异常,让后续的 catch 处理
// return Promise.reject(new Error('Backend Error: ' + response.data.message));
// }
// 务必返回 response 对象 (或 Promise),否则后续的 .then 无法接收到响应
return response;
// 或者直接返回 response.data,这样后续的 .then 就直接拿到数据了
// return response.data;
}, function (error) {
// 对响应错误做点什么 (例如,统一处理 HTTP 状态码错误)
console.error(‘<<< 响应拦截器:收到失败响应!’);
console.error(‘<<< 响应拦截器:错误详情’, error);
// 统一处理 HTTP 状态码
if (error.response) {
switch (error.response.status) {
case 401:
console.error('<<< 响应拦截器:未授权,跳转到登录页...');
// TODO: 跳转到登录页面的逻辑
break;
case 403:
console.error('<<< 响应拦截器:禁止访问!');
// TODO: 显示权限不足提示
break;
case 404:
console.error('<<< 响应拦截器:请求的资源不存在!');
// TODO: 显示资源不存在提示
break;
case 500:
console.error('<<< 响应拦截器:服务器内部错误!');
// TODO: 显示服务器错误提示
break;
default:
console.error(`<<< 响应拦截器:收到未知状态码 ${error.response.status}`);
// TODO: 其他状态码处理
}
} else if (error.request) {
// 网络错误或没有收到响应
console.error('<<< 响应拦截器:网络或无响应错误!', error.message);
// TODO: 提示用户检查网络
} else {
// 其他类型的错误
console.error('<<< 响应拦截器:请求配置或未知错误!', error.message);
}
// 务必返回 Promise.reject(error),以便错误能被后续的 .catch 捕获
return Promise.reject(error);
});
// 使用了上述拦截器的请求示例
axios.get(‘/api/some-data’) // 这个请求会先经过请求拦截器,然后发送,收到响应后先经过响应拦截器
.then(response => {
console.log(‘.then 处理函数:成功获取数据’, response.data); // 如果拦截器返回 response.data,这里就直接是数据了
})
.catch(error => {
console.error(‘.catch 处理函数:捕获到错误’, error); // 如果拦截器抛出了错误或返回 Promise.reject,这里会捕获
});
axios.get(‘/api/non-existent’) // 这个请求会返回 404,进入响应拦截器的 rejected 函数
.then(response => {
console.log(‘.then 处理函数:不会执行’);
})
.catch(error => {
console.error(‘.catch 处理函数:捕获到 404 错误’, error);
});
“`
拦截器与实例
如果你创建了 Axios 实例,拦截器是添加到实例上的,而不是全局的 axios
对象上。这允许你为不同的实例应用不同的拦截器链。
“`javascript
const authApi = axios.create({
baseURL: ‘/api’,
timeout: 5000
});
// 只为 authApi 实例添加请求拦截器,用于添加 Token
authApi.interceptors.request.use(function (config) {
const token = localStorage.getItem(‘authToken’);
if (token) {
config.headers[‘Authorization’] = ‘Bearer ‘ + token;
}
return config;
}, function (error) {
return Promise.reject(error);
});
// 为 authApi 实例添加响应拦截器,用于统一处理 401 未授权错误
authApi.interceptors.response.use(function (response) {
return response;
}, function (error) {
if (error.response && error.response.status === 401) {
console.error(‘Token 过期或未授权,跳转登录!’);
// TODO: 跳转登录
}
return Promise.reject(error);
});
// 使用 authApi 实例发送需要 Token 的请求
authApi.get(‘/sensitive-data’)
.then(…)
.catch(…);
// 使用全局 axios 或其他实例发送不需要 Token 的请求,不会经过 authApi 的拦截器
axios.get(‘/public-data’)
.then(…)
.catch(…);
“`
移除拦截器
添加拦截器会返回一个 ID。你可以使用这个 ID 来移除拦截器:
“`javascript
const myInterceptorId = axios.interceptors.request.use(function(config) {
// …
return config;
});
// 需要时移除
axios.interceptors.request.eject(myInterceptorId);
// 响应拦截器移除类似
const myResponseInterceptorId = axios.interceptors.response.use(function(response) {
// …
return response;
});
axios.interceptors.response.eject(myResponseInterceptorId);
“`
11. 取消请求
在某些场景下,你可能希望取消一个已经发出的请求。例如:
- 用户在请求完成前切换到了另一个页面。
- 用户输入搜索关键字时,快速输入多个字会导致发送多次请求,你可能只希望响应最后一次输入对应的请求。
- 请求超时后,你可能希望主动取消它而不是让它自然失败。
Axios 提供了两种方式来取消请求:CancelToken
(旧方法) 和 AbortController
(新方法)。推荐使用 AbortController
,因为它是一个 Web 标准 API,兼容性越来越好。
使用 CancelToken
(旧方法)
“`javascript
import axios from ‘axios’;
// 1. 创建一个 CancelToken 的 Source
const CancelToken = axios.CancelToken;
const source = CancelToken.source(); // source.token 是用于请求的 token, source.cancel 是用于取消的方法
// 2. 发送请求时,将 source.token 传递给 config 选项
axios.get(‘/api/data’, {
cancelToken: source.token // 将取消令牌传递给请求配置
})
.then(function (response) {
console.log(‘请求成功’, response.data);
})
.catch(function (error) {
// 检查请求是否被取消
if (axios.isCancel(error)) {
console.log(‘请求被取消’, error.message); // error.message 是取消时传递的信息
} else {
// 处理其他错误
console.error(‘请求发生错误’, error.message);
}
});
// 3. 在需要取消请求的地方调用 source.cancel()
// 例如,在一个按钮点击事件中或者在组件销毁时
setTimeout(() => { // 模拟在一定时间后取消请求
source.cancel(‘操作被用户取消’); // 调用 cancel 方法并传递一个取消原因/信息
}, 100); // 100 毫秒后取消
“`
使用 AbortController
(新方法,推荐)
AbortController
是一个 Web 标准 API,用于中止一个或多个 Web 请求。Axios v0.22.0 及以上版本支持通过 signal
配置项与其集成。
“`javascript
import axios from ‘axios’;
// 1. 创建 AbortController 实例
const controller = new AbortController();
const signal = controller.signal; // 获取 signal 对象
// 2. 发送请求时,将 signal 传递给 config 选项
axios.get(‘/api/data’, {
signal: signal // 将 signal 传递给请求配置
})
.then(function (response) {
console.log(‘请求成功’, response.data);
})
.catch(function (error) {
// 检查请求是否被中止 (AbortError 是 AbortController 取消请求抛出的错误类型)
if (error.name === ‘AbortError’) {
console.log(‘请求被中止’);
} else {
// 处理其他错误
console.error(‘请求发生错误’, error.message);
}
});
// 3. 在需要取消请求的地方调用 controller.abort()
// 例如,在一个按钮点击事件中或者在组件销毁时
setTimeout(() => { // 模拟在一定时间后中止请求
controller.abort(); // 调用 abort 方法
console.log(‘调用 controller.abort() 取消请求’);
}, 100); // 100 毫秒后中止
“`
使用 AbortController
的好处是它是标准的浏览器 API,不局限于 Axios,可以用于取消 fetch
请求或其他基于 Signal 的异步操作,更加通用。
12. 发送多个并发请求
有时你需要同时发送多个请求,并在所有请求都完成后进行处理。Axios 提供了 axios.all
和 axios.spread
方法来处理这种情况,但更常见和推荐的方式是使用原生的 Promise.all
。
使用 axios.all
和 axios.spread
(Axios 特有的)
axios.all
接收一个包含多个 Promise (Axios 请求返回的就是 Promise) 的数组,它会返回一个新的 Promise,当数组中的所有 Promise 都解决后,这个新的 Promise 才会解决。axios.spread
是一个辅助函数,用于将 axios.all
结果数组展开成多个参数传递给 .then()
回调函数。
“`javascript
import axios from ‘axios’;
function getPost(id) {
return axios.get(https://jsonplaceholder.typicode.com/posts/${id}
);
}
function getUser(id) {
return axios.get(https://jsonplaceholder.typicode.com/users/${id}
);
}
// 同时发送两个请求
axios.all([getPost(1), getUser(1)]) // 传递一个 Promise 数组
.then(axios.spread(function (postResponse, userResponse) {
// 当所有请求都成功后,这里的回调函数会被调用
// axios.spread 会将结果数组 [postResponse, userResponse] 展开为参数
console.log(‘所有并发请求成功!’);
console.log(‘文章数据:’, postResponse.data);
console.log(‘用户数据:’, userResponse.data);
}))
.catch(function (error) {
// 只要其中一个请求失败,就会进入 catch
console.error(‘有并发请求失败!’, error);
});
“`
使用 Promise.all
(更常用,推荐)
Promise.all
是原生的 JavaScript API,功能与 axios.all
类似,接收一个 Promise 数组,返回一个 Promise。当所有 Promise 都解决时,返回的 Promise 解决,值为一个包含所有解决值的数组。当任何一个 Promise 拒绝时,返回的 Promise 拒绝,值为第一个拒绝的原因。
“`javascript
import axios from ‘axios’;
function getPost(id) {
return axios.get(https://jsonplaceholder.typicode.com/posts/${id}
);
}
function getUser(id) {
return axios.get(https://jsonplaceholder.typicode.com/users/${id}
);
}
// 同时发送两个请求
Promise.all([getPost(1), getUser(1)]) // 传递一个 Promise 数组
.then(function (results) {
// results 是一个数组,包含所有 Promise resolved 的值
// 在这里 results[0] 是 postResponse 对象, results[1] 是 userResponse 对象
console.log(‘所有并发请求成功!’);
const postResponse = results[0];
const userResponse = results[1];
console.log(‘文章数据:’, postResponse.data);
console.log(‘用户数据:’, userResponse.data);
})
.catch(function (error) {
// 只要其中一个请求失败,就会进入 catch
console.error(‘有并发请求失败!’, error);
});
“`
如果你使用 async/await
,结合 Promise.all
会更简洁:
“`javascript
import axios from ‘axios’;
async function fetchPostAndUser(postId, userId) {
try {
// 同时 await 多个 Promise.all
const [postResponse, userResponse] = await Promise.all([
axios.get(https://jsonplaceholder.typicode.com/posts/${postId}
),
axios.get(https://jsonplaceholder.typicode.com/users/${userId}
)
]);
console.log('所有并发请求成功 (async/await)!');
console.log('文章数据:', postResponse.data);
console.log('用户数据:', userResponse.data);
return {
post: postResponse.data,
user: userResponse.data
};
} catch (error) {
console.error('有并发请求失败 (async/await)!', error);
throw error; // 抛出错误
}
}
fetchPostAndUser(1, 1);
fetchPostAndUser(1, 999); // 演示其中一个失败的情况
“`
使用 Promise.all
更符合现代 JavaScript 的 Promise 实践,且用途更广泛,不仅限于 Axios 请求。因此,通常推荐使用 Promise.all
来管理多个并发的 Axios 请求。
13. 总结与进阶
恭喜你!通过阅读本文,你已经学习了 Axios 的基础用法,包括安装、发送不同类型的请求、配置请求、处理错误、结合 async/await
、创建实例、使用拦截器以及处理并发请求和请求取消。这些知识足以让你在绝大多数前端项目中进行高效、健壮的网络数据交互。
Axios 还有一些本文没有详细展开但也很实用的特性,例如:
- 转换请求/响应数据 (Transformers): 可以自定义请求发送前的数据序列化和响应返回后的数据反序列化逻辑。
- 上传/下载进度事件: 可以在请求配置中监听
onUploadProgress
和onDownloadProgress
事件来获取上传或下载的进度信息。
如果想深入了解这些内容或其他高级用法,建议查阅 Axios 的官方文档。
现在,是时候在你的项目中实践这些知识了!尝试使用 Axios 替换掉原有的 fetch
或 XMLHttpRequest
代码,体验它带来的便利和强大。祝你在前端开发的道路上越走越顺!