前端面试六之axios
一、axios简介
Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js 环境。在浏览器端,Axios 的底层实现是基于原生的 XMLHttpRequest
(XHR)。它对 XHR 进行了封装,增加了 Promise 支持、自动转换 JSON 数据、请求和响应拦截器等功能。在 Node.js 环境中,Axios 使用 http
模块来发起请求。
1.1XHR、Fetch API 和 Axios 的区别
1. XMLHttpRequest (XHR)
-
定义:XHR 是一个较旧的 API,用于在浏览器中发起网络请求。
-
工作原理:基于事件驱动,通过监听
onreadystatechange
事件来处理请求状态变化。 -
优点:
-
兼容性好,支持较旧的浏览器。
-
提供了丰富的 API,可以实现复杂的网络请求操作。
-
-
缺点:
-
基于回调,容易导致回调地狱,代码难以维护。
-
无法中止请求。
-
需要手动设置请求头和解析响应。
-
-
使用场景:适用于需要兼容旧浏览器的场景。
Fetch API
-
定义:Fetch 是一个现代的、基于 Promise 的网络请求接口,用于替代 XHR。
-
工作原理:基于 Promise,使用
Promise
对象来处理异步请求。 -
优点:
-
语法简洁,使用 Promise 避免了回调地狱。
-
支持中止请求(通过
AbortController
)。 -
更好的错误处理。
-
支持流式传输,适用于大文件上传和下载。
-
-
缺点:
-
兼容性较差,在一些较老的浏览器中不支持。
-
响应处理需要手动调用
.json()
或.text()
等方法。 -
默认不接受跨域请求,需要通过 CORS 配置。
-
-
使用场景:适用于现代 Web 开发,不需要考虑较旧浏览器版本的场景。
3. Axios
-
定义:Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js 环境。
-
工作原理:本质上是对 XHR 的封装,在浏览器端使用 XHR,在 Node.js 中使用
http
模块。 -
优点:
-
基于 Promise,支持
async/await
。 -
自动转换 JSON 数据。
-
支持请求和响应拦截器。
-
能够取消请求。
-
-
缺点:
-
需要额外引入库。
-
在一些极端情况下(如超时控制)可能不如原生 Fetch 灵活。
-
-
使用场景:适用于需要更丰富的功能(如拦截器、自动数据转换)的场景。
1.2axios基于 Promise
Axios 的请求返回的是一个 Promise 对象,这使得它能够很方便地进行异步操作的处理。例如,你可以使用 .then()
方法来处理请求成功的情况,使用 .catch()
方法来处理请求失败的情况。比如:
axios.get('/user').then(response => {console.log(response.data); // 处理成功响应}).catch(error => {console.error(error); // 处理错误});
二、演示get和post
<template><div><h1>Axios GET 和 POST 示例</h1><button @click="fetchData">获取数据 (GET)</button><button @click="postData">提交数据 (POST)</button><div v-if="data"><h2>获取的数据:</h2><pre>{{ data }}</pre></div><div v-if="postDataResponse"><h2>POST 请求的响应:</h2><pre>{{ postDataResponse }}</pre></div></div>
</template><script>
import axios from 'axios';export default {data() {return {data: null, // 用于存储 GET 请求的响应数据postDataResponse: null, // 用于存储 POST 请求的响应数据};},methods: {// 发起 GET 请求fetchData() {axios.get('https://jsonplaceholder.typicode.com/posts/1').then(response => {this.data = response.data; // 将响应数据存储到 data 中}).catch(error => {console.error('GET 请求失败:', error);});},// 发起 POST 请求postData() {const postData = {title: 'foo',body: 'bar',userId: 1,};axios.post('https://jsonplaceholder.typicode.com/posts', postData).then(response => {this.postDataResponse = response.data; // 将响应数据存储到 postDataResponse 中}).catch(error => {console.error('POST 请求失败:', error);});},},
};
</script><style>
/* 添加一些简单的样式 */
button {margin-right: 10px;
}
</style>
(1)GET
请求
-
使用
axios.get
方法发起GET
请求。 -
请求的 URL 是
https://jsonplaceholder.typicode.com/posts/1
,这是一个公共的测试 API,返回一个模拟的帖子数据。 -
请求成功后,将响应数据存储到
data
属性中,并在页面上显示。
(2)POST
请求
-
使用
axios.post
方法发起POST
请求。 -
请求的 URL 是
https://jsonplaceholder.typicode.com/posts
,同样是一个公共的测试 API。 -
请求体是一个包含
title
、body
和userId
的对象。 -
请求成功后,将响应数据存储到
postDataResponse
属性中,并在页面上显示。
使用 async/await
methods: {async fetchData() {try {const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');this.data = response.data;} catch (error) {console.error('GET 请求失败:', error);}},async postData() {const postData = {title: 'foo',body: 'bar',userId: 1,};try {const response = await axios.post('https://jsonplaceholder.typicode.com/posts', postData);this.postDataResponse = response.data;} catch (error) {console.error('POST 请求失败:', error);}},
},
三、发送带有请求头和查询参数的请求
<template><div><h1>Axios 请求头和查询参数示例</h1><button @click="fetchWithHeadersAndParams">发送请求</button><div v-if="response"><h2>响应数据:</h2><pre>{{ response }}</pre></div></div>
</template><script>
import axios from 'axios';export default {data() {return {response: null, // 用于存储响应数据};},methods: {// 发送带有请求头和查询参数的 GET 请求fetchWithHeadersAndParams() {// 定义请求头const headers = {'Authorization': 'Bearer your_token_here','Custom-Header': 'CustomValue',};// 定义查询参数const params = {userId: 1,limit: 10,status: 'active',};// 发起请求axios.get('https://jsonplaceholder.typicode.com/posts', {headers: headers, // 添加请求头params: params, // 添加查询参数}).then(response => {this.response = response.data; // 将响应数据存储到 response 中}).catch(error => {console.error('请求失败:', error);this.response = { error: error.message }; // 如果失败,存储错误信息});},},
};
</script><style>
button {margin-right: 10px;
}
</style>
(1)请求头
-
在
axios.get
方法中,通过headers
属性传递自定义请求头。 -
示例中添加了
Authorization
和Custom-Header
两个请求头。
(2)查询参数
-
在
axios.get
方法中,通过params
属性传递查询参数。 -
查询参数会自动被 Axios 转换为 URL 的查询字符串。例如,
params
对象{ userId: 1, limit: 10 }
会被转换为?userId=1&limit=10
。
(3)完整的请求 URL
假设请求头和查询参数都设置好后,最终的请求 URL 会类似于:
https://jsonplaceholder.typicode.com/posts?userId=1&limit=10&status=active
使用 async/await
methods: {async fetchWithHeadersAndParams() {const headers = {'Authorization': 'Bearer your_token_here','Custom-Header': 'CustomValue',};const params = {userId: 1,limit: 10,status: 'active',};try {const response = await axios.get('https://jsonplaceholder.typicode.com/posts', {headers: headers,params: params,});this.response = response.data;} catch (error) {console.error('请求失败:', error);this.response = { error: error.message };}},
},
四、发送不同格式的请求体
在 Vue 项目中使用 Axios 发送请求时,可以通过配置请求体的格式来满足不同的需求。以下是三种常见的请求体格式的示例:application/x-www-form-urlencoded
、multipart/form-data
和 application/json
。
<template><div><h1>Axios 请求体格式示例</h1><button @click="sendUrlEncoded">发送 URL 编码数据</button><button @click="sendMultipart">发送 Multipart 数据</button><button @click="sendJson">发送 JSON 数据</button><div v-if="response"><h2>响应数据:</h2><pre>{{ response }}</pre></div></div>
</template><script>
import axios from 'axios';export default {data() {return {response: null, // 用于存储响应数据};},methods: {// 发送 URL 编码数据sendUrlEncoded() {const data = new URLSearchParams();data.append('username', 'kimi');data.append('password', '123456');axios.post('https://jsonplaceholder.typicode.com/posts', data).then(response => {this.response = response.data;}).catch(error => {console.error('请求失败:', error);this.response = { error: error.message };});},// 发送 Multipart 数据sendMultipart() {const formData = new FormData();formData.append('file', new Blob(['Hello World'], { type: 'text/plain' }), 'example.txt');formData.append('description', 'This is a test file');axios.post('https://jsonplaceholder.typicode.com/posts', formData, {headers: {'Content-Type': 'multipart/form-data',},}).then(response => {this.response = response.data;}).catch(error => {console.error('请求失败:', error);this.response = { error: error.message };});},// 发送 JSON 数据sendJson() {const data = {title: 'foo',body: 'bar',userId: 1,};axios.post('https://jsonplaceholder.typicode.com/posts', data).then(response => {this.response = response.data;}).catch(error => {console.error('请求失败:', error);this.response = { error: error.message };});},},
};
</script><style>
button {margin-right: 10px;
}
</style>
(1)发送 URL 编码数据
-
使用
URLSearchParams
构造请求体。 -
Axios 会自动将
URLSearchParams
转换为application/x-www-form-urlencoded
格式。 -
示例代码:
const data = new URLSearchParams(); data.append('username', 'kimi'); data.append('password', '123456');axios.post('https://jsonplaceholder.typicode.com/posts', data);
(2)发送 Multipart 数据
-
使用
FormData
构造请求体。 -
需要手动设置请求头
Content-Type
为multipart/form-data
。 -
示例代码:
const formData = new FormData(); formData.append('file', new Blob(['Hello World'], { type: 'text/plain' }), 'example.txt'); formData.append('description', 'This is a test file');axios.post('https://jsonplaceholder.typicode.com/posts', formData, {headers: {'Content-Type': 'multipart/form-data',}, });
(3)发送 JSON 数据
-
直接发送一个 JavaScript 对象。
-
Axios 会自动将对象序列化为 JSON 格式,并设置请求头
Content-Type
为application/json
。 -
示例代码:
const data = {title: 'foo',body: 'bar',userId: 1, };axios.post('https://jsonplaceholder.typicode.com/posts', data);
五、axios默认配置项
以下是一些常用的 Axios 默认配置项:
配置项 | 描述 |
---|---|
baseURL | 基础 URL,请求时会自动拼接 |
headers | 请求头,可以设置通用头或特定方法的头 |
timeout | 请求超时时间(单位为毫秒) |
withCredentials | 是否允许跨域请求携带凭证(如 cookies) |
responseType | 响应数据类型(如 json 、text 、arraybuffer 等) |
validateStatus | 定义哪些状态码被视为成功响应 |
transformRequest | 请求发送前的数据转换函数 |
transformResponse | 响应数据的转换函数 |
以下是一个完整的示例,展示如何设置全局默认配置和自定义实例默认配置:
// 设置全局默认配置
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = 'Bearer your_token_here';
axios.defaults.timeout = 3000;// 创建自定义实例
const customInstance = axios.create({baseURL: 'https://api.custom.com',headers: {'Content-Type': 'application/json'}
});// 修改实例的默认配置
customInstance.defaults.timeout = 5000;// 使用全局配置发起请求
axios.get('/user').then(response => {console.log(response.data);
});// 使用自定义实例发起请求
customInstance.get('/custom-user').then(response => {console.log(response.data);
});
1. 全局默认配置
Axios 允许通过 axios.defaults
设置全局默认配置,这些配置将应用于所有通过 axios
发起的请求。以下是一些常见的全局默认配置:
基础 URL (baseURL
):为所有请求设置一个基础 URL,请求的 URL 会自动拼接在 baseURL
后面。
axios.defaults.baseURL = 'https://api.example.com';
请求头 (headers
):设置通用的请求头或特定方法的请求头。
axios.defaults.headers.common['Authorization'] = 'Bearer your_token_here';
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
超时时间 (timeout
):设置请求的超时时间(单位为毫秒),超过该时间未响应则请求会被中断。
axios.defaults.timeout = 2500; // 2.5 秒
验证状态码 (validateStatus
):定义哪些 HTTP 状态码被视为成功响应。
axios.defaults.validateStatus = function (status) {return status >= 200 && status < 300; // 默认值
};
2. 自定义实例默认配置
除了全局配置,Axios 还允许创建自定义实例,并为每个实例设置默认配置。这在项目中需要使用多个不同配置的 Axios 实例时非常有用:
const instance = axios.create({baseURL: 'https://api.example.com',timeout: 3000,headers: {'Authorization': 'Bearer your_token_here'}
});// 修改实例的默认配置
instance.defaults.headers.common['Custom-Header'] = 'CustomValue';
3. 配置的优先级
Axios 的配置会按优先级合并,优先级从低到高依次为:
-
库默认值:在
lib/defaults.js
中定义的默认值。 -
实例的
defaults
属性:通过axios.create()
创建的实例的默认配置。 -
请求的
config
参数:在每次请求中传递的配置参数。const instance = axios.create({timeout: 2500 // 实例默认超时时间 });instance.get('/longRequest', {timeout: 5000 // 该请求的超时时间覆盖实例默认值 });
六、axios响应数据的格式和状态码
在使用 Axios 发起 HTTP 请求时,响应数据的格式和状态码是两个非常重要的部分。Axios 提供了丰富的响应数据结构,同时允许你根据状态码来处理不同的响应情况。
1. Axios 响应格式
当 Axios 发起请求并收到响应时,它会返回一个响应对象,该对象包含以下属性:
-
data
:响应体的内容。Axios 会根据Content-Type
自动解析响应体。如果是application/json
,它会被解析为 JavaScript 对象。 -
status
:HTTP 状态码(如200
、404
、500
等)。 -
statusText
:HTTP 状态消息(如OK
、Not Found
、Internal Server Error
等)。 -
headers
:响应头,是一个对象,包含服务器返回的所有响应头。 -
config
:请求的配置对象,包含了请求时传递的配置信息。 -
request
:原生的请求对象(在浏览器中是XMLHttpRequest
,在 Node.js 中是http.ClientRequest
)。
示例:响应格式
以下是一个示例,展示如何处理 Axios 的响应数据:
import axios from 'axios';axios.get('https://jsonplaceholder.typicode.com/posts/1').then(response => {console.log('响应数据:', response.data); // 响应体内容console.log('状态码:', response.status); // HTTP 状态码console.log('状态消息:', response.statusText); // HTTP 状态消息console.log('响应头:', response.headers); // 响应头console.log('请求配置:', response.config); // 请求配置}).catch(error => {console.error('请求失败:', error);});
2. 状态码处理
HTTP 状态码是服务器对请求的响应状态的描述。常见的状态码包括:
-
2xx:成功
-
200 OK
:请求成功。 -
201 Created
:请求成功,且服务器创建了新资源。 -
204 No Content
:请求成功,但没有返回内容。
-
-
3xx:重定向
-
301 Moved Permanently
:请求的资源已永久移动到新位置。 -
302 Found
:请求的资源临时移动到新位置。
-
-
4xx:客户端错误
-
400 Bad Request
:请求格式错误。 -
401 Unauthorized
:未授权,需要认证。 -
403 Forbidden
:禁止访问。 -
404 Not Found
:请求的资源不存在。
-
-
5xx:服务器错误
-
500 Internal Server Error
:服务器内部错误。 -
502 Bad Gateway
:网关错误。 -
503 Service Unavailable
:服务不可用。
-
示例:根据状态码处理响应
以下是一个示例,展示如何根据状态码处理不同的响应情况:
import axios from 'axios';axios.get('https://jsonplaceholder.typicode.com/posts/1').then(response => {console.log('响应数据:', response.data);// 根据状态码处理响应switch (response.status) {case 200:console.log('请求成功:', response.data);break;case 404:console.error('资源未找到');break;case 500:console.error('服务器内部错误');break;default:console.log('其他状态码:', response.status);}}).catch(error => {console.error('请求失败:', error);// 处理错误响应if (error.response) {console.error('状态码:', error.response.status);console.error('状态消息:', error.response.statusText);} else if (error.request) {console.error('请求已发送,但未收到响应');} else {console.error('请求配置错误:', error.message);}});
3. 自定义状态码处理
你可以通过 validateStatus
配置项自定义哪些状态码被视为成功响应。默认情况下,只有 2xx
状态码被视为成功,但你可以修改这个行为:
import axios from 'axios';axios.get('https://jsonplaceholder.typicode.com/posts/1', {validateStatus: function (status) {return status >= 200 && status < 400; // 将 2xx 和 3xx 状态码视为成功}
})
.then(response => {console.log('响应数据:', response.data);
})
.catch(error => {console.error('请求失败:', error);
});
七、Axios 的请求拦截器和响应拦截器
Axios 的请求拦截器和响应拦截器是 Axios 提供的强大功能,用于在请求发送之前和响应返回之后对数据进行处理。它们可以用于多种场景,例如统一设置请求头、添加认证信息、处理错误、格式化响应数据等。
1. 请求拦截器
请求拦截器可以在请求发送之前对请求进行处理。你可以通过 axios.interceptors.request.use()
方法添加请求拦截器。
示例:添加请求拦截器
以下是一个示例,展示如何在请求拦截器中统一设置请求头和添加认证信息:
import axios from 'axios';// 添加请求拦截器
axios.interceptors.request.use(config => {// 在发送请求之前做些什么config.headers['Authorization'] = 'Bearer your_token_here'; // 添加认证信息config.headers['Custom-Header'] = 'CustomValue'; // 添加自定义请求头return config;},error => {// 对请求错误做些什么return Promise.reject(error);}
);// 测试请求
axios.get('https://jsonplaceholder.typicode.com/posts/1').then(response => {console.log('响应数据:', response.data);}).catch(error => {console.error('请求失败:', error);});
2. 响应拦截器
响应拦截器可以在响应返回之后对响应进行处理。你可以通过 axios.interceptors.response.use()
方法添加响应拦截器。
示例:添加响应拦截器
以下是一个示例,展示如何在响应拦截器中统一处理响应数据和错误:
import axios from 'axios';// 添加响应拦截器
axios.interceptors.response.use(response => {// 对响应数据做点什么console.log('响应状态码:', response.status);return response.data; // 只返回响应数据},error => {// 对响应错误做点什么if (error.response) {// 请求已发出,但服务器响应的状态码不在 2xx 范围内console.error('响应状态码:', error.response.status);console.error('响应消息:', error.response.statusText);} else if (error.request) {// 请求已发出,但没有收到响应console.error('请求已发出,但未收到响应');} else {// 在设置请求时发生了一些事情,触发了一个错误console.error('请求配置错误:', error.message);}return Promise.reject(error);}
);// 测试请求
axios.get('https://jsonplaceholder.typicode.com/posts/1').then(response => {console.log('响应数据:', response);}).catch(error => {console.error('请求失败:', error);});
3. 自定义实例的拦截器
除了全局拦截器,你还可以为自定义的 Axios 实例添加拦截器。这在项目中需要使用多个不同配置的 Axios 实例时非常有用。
示例:为自定义实例添加拦截器
import axios from 'axios';// 创建自定义实例
const customInstance = axios.create({baseURL: 'https://api.example.com',timeout: 3000,
});// 为自定义实例添加请求拦截器
customInstance.interceptors.request.use(config => {config.headers['Authorization'] = 'Bearer your_token_here';return config;},error => {return Promise.reject(error);}
);// 为自定义实例添加响应拦截器
customInstance.interceptors.response.use(response => {return response.data; // 只返回响应数据},error => {console.error('请求失败:', error);return Promise.reject(error);}
);// 使用自定义实例发起请求
customInstance.get('/posts/1').then(response => {console.log('响应数据:', response);}).catch(error => {console.error('请求失败:', error);});
4. 移除拦截器
如果需要移除某个拦截器,可以通过返回的 id
来移除它:
import axios from 'axios';// 添加请求拦截器并获取 id
const requestInterceptorId = axios.interceptors.request.use(config => {config.headers['Authorization'] = 'Bearer your_token_here';return config;},error => {return Promise.reject(error);}
);// 移除请求拦截器
axios.interceptors.request.eject(requestInterceptorId);// 添加响应拦截器并获取 id
const responseInterceptorId = axios.interceptors.response.use(response => {return response.data;},error => {return Promise.reject(error);}
);// 移除响应拦截器
axios.interceptors.response.eject(responseInterceptorId);
5. 使用场景
(1)请求拦截器
-
统一设置请求头:例如,添加认证信息(如
Authorization
)、内容类型(如Content-Type
)等。 -
添加公共参数:例如,为每个请求添加时间戳或用户 ID。
-
请求取消:通过
CancelToken
或AbortController
实现请求取消功能。
(2)响应拦截器
-
统一处理响应数据:例如,只返回响应体中的数据部分。
-
统一处理错误:例如,根据状态码显示不同的错误消息。
-
处理重定向:例如,根据状态码(如
401
)跳转到登录页面。