从http到Axios和fetch全解析
乜嘢是HTTP
HTTP,全称为HyperText Transfer Protocol(超文本传输协议),是互联网上应用最为广泛的一种网络协议。它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器,并且支持多媒体内容的传输,如图片、视频等。
基本概念
-
HTTP是一种基于请求与响应的应用层协议,它是无状态的,意味着每个请求都是独立的,服务器不会保存客户端的状态信息。
-
通常,用户通过客户端(如浏览器)输入URL发起请求,服务器接收到请求后进行处理,并将处理结果包装成HTTP响应消息返回给客户端,客户端再根据响应内容渲染页面展示给用户。
工作流程
HTTP的工作流程包括四个主要步骤:
- 客户端发起请求
- 服务器处理请求
- 服务器返回响应
- 客户端渲染页面
在这个过程中,客户端和服务端之间交换的消息被称为请求和响应。请求是由接受方——通常是Web浏览器——发起的,而响应则是由服务器发出的回复。
HTTP的特点
- 媒体独立:只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。
- 无连接:限制每次连接只处理一个请求,服务器处理完客户的请求并收到应答后即断开连接,节省传输时间。
- 无状态:对于事务处理没有记忆能力,这意味着每个请求都必须包含足够的信息让服务器理解并处理该请求。
HTTP方法
HTTP协议定义了几种不同的方法(也称作“动作”),用于以不同方式操作指定的资源。最常用的包括:
GET
:用于获取资源。POST
:用于提交数据,比如填写表单或上传文件。PUT
:用于更新资源。DELETE
:用于删除资源。HEAD
:类似于GET,但只请求消息头,不包括消息体。OPTIONS
:查询服务器支持的方法。TRACE
:回显服务器收到的请求,主要用于测试或诊断。
HTTP请求报文
一个HTTP请求报文通常由四部分组成:请求行、请求头部、空行以及请求数据(可选)。
-
请求行(Request Line):这是请求报文的第一行,包含了三个元素:
- 请求方法(如GET、POST等),指示要执行的操作类型。
- 请求的URL或路径,指向目标资源的位置。
- HTTP协议版本号,表示使用的HTTP协议版本。
例如,
GET /index.html HTTP/1.1
表示使用GET方法请求根目录下的index.html文件,并且使用的是HTTP/1.1版本。 -
请求头部(Headers):紧接着请求行之后是一系列的头部字段,它们提供了关于请求的更多信息,比如客户端支持的内容类型、语言偏好、编码方式等。每个头部字段都由名称和值组成,以冒号分隔。
-
空行(Blank Line):在所有头部字段之后是一个空行,它标志着头部部分的结束和可能存在的请求体部分的开始。
-
请求数据(Optional Request Body):对于某些请求方法(如POST),这里会包含发送给服务器的数据,比如表单数据或者文件上传内容。
HTTP响应报文
响应报文也有类似的结构,但它的起始行被称为状态行,而不是请求行。
-
状态行(Status Line):包括三部分:
- HTTP协议版本号。
- 状态码,三位数字代码,用于描述请求的结果(例如200代表成功,404代表未找到资源)。
- 原因短语,是对状态码的文字解释。
-
响应头部(Response Headers):类似于请求头部,这些字段提供额外的信息,比如响应的日期、内容长度、内容类型等。
-
空行:同样,在所有的响应头部之后有一个空行。
-
响应体(Response Body):如果有的话,这部分包含了实际返回给客户端的数据,比如HTML文档、图片或者其他类型的文件。
HTTP常见响应状态码
HTTP响应状态码是服务器在接收到客户端请求后返回的一个三位数字代码,它用来表示请求的处理情况。这些状态码分为五类,每类都有其特定的意义和用途。以下是各类常见的HTTP状态码及其含义:
1xx(信息性状态码)
- 100 Continue:客户端应当继续发送请求。
- 101 Switching Protocols:服务器已经理解了客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求。
2xx(成功状态码)
- 200 OK:请求已成功,且请求的信息包含在响应中。
- 201 Created:请求已经被实现,而且有一个新的资源被创建。
- 204 No Content:服务器成功处理了请求,但没有返回任何内容。
3xx(重定向状态码)
- 301 Moved Permanently:请求的资源已被永久地移动到新URI,返回信息会包括新的URI。
- 302 Found:请求的资源现在临时从不同的URI响应请求。
- 304 Not Modified:如果客户端发送了一个条件GET请求,而该请求的资源自上次访问以来未被修改,则使用此状态码。
4xx(客户端错误状态码)
- 400 Bad Request:由于语法格式有误,服务器无法理解此请求。
- 401 Unauthorized:当前请求需要用户验证。
- 403 Forbidden:服务器已经理解请求,但是拒绝执行它。
- 404 Not Found:服务器找不到与请求URI相匹配的任何资源。
5xx(服务端错误状态码)
- 500 Internal Server Error:服务器遇到了一个未曾预料的情况,导致了其无法完成对请求的处理。
- 501 Not Implemented:服务器不支持当前请求所需要的功能。
- 503 Service Unavailable:由于临时的服务器维护或者过载,服务器当前无法处理请求。
URL
?
URL,全称为Uniform Resource Locator,即统一资源定位符,是用于标识互联网上资源位置的字符串。它允许用户通过网络访问网页、图片、视频等资源。URL是URI(统一资源标识符)的一个种类,专门用来表示资源的位置。
一个标准的URL通常由以下几个部分组成:
-
协议(Scheme):指定使用的通信协议,比如HTTP(超文本传输协议)、HTTPS(安全的超文本传输协议)、FTP(文件传输协议)等。协议后面跟有一个冒号和两个斜杠(😕/)。
示例:
https://
-
主机名(Host):指明了资源所在的服务器地址,可以是域名(如www.example.com)或IP地址。
示例:
www.example.com
-
端口号(Port):指定了与服务器连接时所使用的端口。大多数时候,如果使用的是默认端口(例如HTTP默认80,HTTPS默认443),则不需要明确写出端口号。
示例:
:8080
-
路径(Path):指出请求的具体资源在服务器上的位置。它模拟了一个文件系统路径的概念。
示例:
/path/to/resource
-
查询字符串(Query String):以问号(?)开始,后面跟着一系列键值对,用来向服务器传递参数。
示例:
?key=value&foo=bar
-
片段标识符(Fragment Identifier):以井字号(#)开头,指向文档中的某一部分,主要用于HTML页面内部导航。
示例:
#section1
https://www.example.com:8080/path/to/resource?key=value#section1
在这个例子中,https
是协议,www.example.com
是主机名,:8080
是端口号,/path/to/resource
是路径,?key=value
是查询字符串,而 #section1
是片段标识符。
关于回调函数
什么是回调函数?
回调函数是一种编程模式,它允许一个函数作为参数传递给另一个函数,并在特定条件下被调用。
这种机制使得代码可以更加灵活和动态,因为它允许外部定义的行为在内部逻辑的关键点上被执行。
回调函数的主要用途包括:
- 处理异步操作:在网络请求、文件读取等场景中,这些操作通常是异步进行的,即它们不会立即完成。为了获取这些操作的结果,我们可以将一个回调函数作为参数传递给负责执行该操作的函数。当异步操作完成后,这个回调函数就会被调用,并传入操作的结果作为参数。
- 确保代码顺序执行:即使在同步代码中,回调函数也可以用来保证某些函数只有在另一些函数执行完毕后才会被执行。
- 事件监听器:在前端开发中,回调函数常用于设置事件监听器。比如,当你想在用户点击按钮时执行特定代码,你可以将这段代码封装在一个函数中,并将其作为回调函数添加到按钮的点击事件上。这样,每当用户触发点击事件时,相应的回调函数就会自动执行。
- 数据处理:回调函数还可以用于数组的数据处理方法中,如
map()
、filter()
、reduce()
等。这些方法接受一个回调函数作为参数,该回调函数定义了如何对数组中的每个元素进行转换或其他操作。
举个栗子
function fetchData(callback) {
// 创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
// 初始化请求
xhr.open('GET', 'https://api.example.com/data', true);
// 设置响应类型为 JSON
xhr.responseType = 'json';
// 设置回调函数来处理服务器响应
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) { // 请求成功完成
callback(null, xhr.response); // 调用回调函数并传递结果数据
} else if (xhr.readyState === 4) { // 请求完成但有错误发生
callback(new Error("Request failed with status " + xhr.status), null);
}
};
// 发送请求
xhr.send();
}
// 定义回调函数
function handleData(error, data) {
if (error) {
console.error("获取数据时出错:", error.message);
return;
}
console.log("获取的数据是: ", data);
}
// 调用fetchData,并传递回调函数handleData作为参数
fetchData(handleData);
我的理解:先在fetchData里面判断服务器响应状态,再将情况作为参数(callback)回调给handleData,此时handleData再执行
具体步骤如下:
-
调用
fetchData(handleData)
:首先,您调用了fetchData
函数,并将handleData
作为参数传入。此时,handleData
并未执行,只是被当作一个函数指针或引用传给了fetchData
。 -
初始化和发送请求:在
fetchData
内部,创建了一个XMLHttpRequest
对象并配置了相应的请求细节(如方法、URL等),然后发送了请求。在这个阶段,程序不会等待服务器响应,而是继续执行后续代码(如果有),因为这是一个异步操作。 -
监听状态变化:通过设置
xhr.onreadystatechange
,为可能发生的事件(即服务器响应到达时)注册了一个监听器。这个监听器会在每次xhr.readyState
发生变化时被触发。 -
处理服务器响应:
- 当
xhr.readyState
变为4且xhr.status
为200时,表示请求成功完成。此时,使用callback(null, xhr.response)
调用之前传入的回调函数(即handleData
),并将获取的数据作为参数传递。 - 如果请求完成但出现了错误(例如,
xhr.status
不是200),则调用callback(new Error("..."), null)
,以错误信息作为第一个参数传递给回调函数。
- 当
-
执行回调函数
handleData
:只有当服务器响应到达,并且根据响应的状态决定如何调用回调之后,才会执行handleData
。它会检查是否有错误发生,并相应地处理数据或错误信息。
值得注意的是
此外,值得注意的是,虽然回调函数提供了灵活性,但过度使用会导致“回调地狱”问题——即多层嵌套的回调函数导致代码难以维护和理解。
什么是AJAX
AJAX(Asynchronous JavaScript and XML)是一种用于创建快速动态网页的技术。
主要特点
-
异步通信:这是AJAX的核心特性之一。它允许网页在不重新加载整个页面的情况下通过与服务器交换少量数据来更新部分页面内容。这使得用户体验更加流畅,因为用户不需要等待整个页面刷新就可以看到更新的信息。
-
提高用户体验:由于其能够局部更新网页内容,因此可以提供更快速的反馈给用户,减少了用户等待的时间。
-
减少带宽使用:通过只请求和传输必要的数据而非整个页面的内容,可以有效减少服务器和客户端之间的数据传输量。
工作流程
-
创建XMLHttpRequest对象:这是进行异步通信的核心对象,负责发送请求并接收响应。
-
发送请求到服务器:可以通过GET或POST方法向服务器发送请求,传递必要的参数。
-
服务器处理请求并返回响应:服务器端脚本处理请求,并将结果以HTML、XML、JSON等形式返回给客户端。
-
更新网页内容:根据从服务器接收到的数据,使用JavaScript和DOM更新网页的部分内容。
应用场景
- 实时验证表单输入:例如检查用户名是否可用,而无需提交整个表单。
- 自动完成搜索框:当用户开始输入时,提供可能的匹配项列表。
- 动态加载分页内容:点击下一页按钮时,仅加载新页面的内容而不是刷新整个页面。
- 消息通知系统:如社交媒体中的新消息提醒,无需刷新页面即可显示新消息。
对,是promise
Promise的概念
Promise是异步编程的一种解决方案
,通俗讲,Promise是一个许诺、承诺
,是对未来事情的承诺,承诺不一定能完成,但是无论是否能完成都会有一个结果。
在JavaScript中,它代表了一个异步操作的最终完成(或失败)及其结果值。一个Promise对象有三种状态:
- Pending(进行中):初始状态,既不是成功,也不是失败。
- Fulfilled(已成功):操作成功完成。
- Rejected(已失败):操作失败。
一旦一个Promise被resolved(无论是fulfilled还是rejected),它的状态就会被锁定,并且结果值或错误信息将不可改变。
Promise的优点
- 更好的错误处理:使用
try/catch
机制和链式.catch()
方法使得错误处理更加直观和易于管理。 - 避免回调地狱:相比于嵌套的回调函数,Promise允许你以更线性、更清晰的方式编写异步代码。
- 链式调用:可以方便地链接多个异步操作,每个操作都等待前一个操作完成后才开始,并能够传递数据。
- 内置支持:现代浏览器和Node.js环境对Promise提供了原生支持,无需额外库。
Promise的缺点
- 复杂度:虽然Promise简化了某些类型的异步编程问题,但对于初学者来说,理解Promise的状态转换和如何正确使用它们可能具有一定的挑战性。
- 无法取消:一旦创建了一个Promise,就无法取消它。这意味着如果需要停止某个异步操作,必须依赖外部逻辑来实现。
- 调试困难:尽管Promise改善了错误处理,但当出现错误时,定位到具体的出错点可能不如同步代码那样直观。
- 资源管理:在某些情况下,特别是在涉及大量并发异步操作时,确保正确的资源管理和释放可能会变得复杂。
Promise的基本使用
创建一个Promise
要创建一个新的Promise实例,你需要使用new Promise()
构造函数,它接受一个执行器函数(executor function)作为参数。这个执行器函数本身又接受两个参数:resolve
和reject
。
resolve(value)
:当异步操作成功完成时调用,表示该Promise被fulfilled(已成功),并且可以传递结果值。reject(error)
:当异步操作失败时调用,表示该Promise被rejected(已失败),并传递错误信息。
const myPromise = new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
const success = true; // 假设这是一个异步操作的结果
if (success) {
resolve("操作成功"); // 成功时调用resolve
} else {
reject("操作失败"); // 失败时调用reject
}
}, 1000); // 模拟延迟1秒
});
使用Promise
一旦创建了Promise,你可以通过.then()
方法来处理操作成功的情况,通过.catch()
方法来处理操作失败的情况。
.then(onFulfilled, onRejected)
:接收两个可选的回调函数作为参数。第一个用于处理Promise fulfilled的情况,第二个用于处理rejected的情况。.catch(onRejected)
:专门用于处理Promise rejected的情况。
myPromise
.then(result => {
console.log(result); // 输出: "操作成功"
})
.catch(error => {
console.error(error); // 如果有错误发生,则输出错误信息
});
链式调用
Promise支持链式调用,这使得你可以轻松地将多个异步操作链接在一起。每个.then()
方法都会返回一个新的Promise,这意味着你可以在前一个操作完成后开始下一个操作,并且可以从前一个操作的结果中获取数据传递给下一个操作。
myPromise
.then(result => {
console.log(result); // 输出: "操作成功"
return "新的数据"; // 返回新的数据
})
.then(newResult => {
console.log(newResult); // 输出: "新的数据"
})
.catch(error => {
console.error(error); // 如果有错误发生,则在这里捕获
});
在这个例子中,第二个.then()
会接收到前一个.then()
返回的数据。
错误处理
为了更好地处理错误,通常建议在Promise链的末尾添加一个.catch()
,这样任何未被捕获的错误都可以在这里进行处理。
myPromise
.then(result => {
console.log(result);
throw new Error("故意抛出的错误"); // 故意抛出一个错误
})
.then(newResult => {
console.log(newResult);
})
.catch(error => {
console.error("捕获到错误:", error.message); // 输出: "捕获到错误: 故意抛出的错误"
});
这里,即使错误是在中间的.then()
中发生的,也会被最后的.catch()
捕捉到。
async
和 await
在现代JavaScript中,async
和 await
提供了一种更加简洁和直观的方式来处理异步操作。它们使得代码看起来更像同步代码,从而提高了可读性和维护性。
1) Promise ==> 异步操作的基础
Promise 是 JavaScript 中处理异步操作的一个重要概念,它代表了一个异步操作的最终完成(或失败)及其结果值。通过 Promise,我们可以更清晰地管理异步代码流,而不是使用传统的回调函数方式。
2) await ==> 将异步转为同步风格
await
可以看作是async wait
的缩写,它的主要作用是等待一个 Promise 对象被解决(resolved),从而使异步代码看起来更加同步。await
必须在async
函数内部使用,不能单独存在。它可以跟任何JavaScript表达式一起使用,但最常用于等待一个 Promise 对象。- 如果
await
后面跟着的是一个普通的表达式而非 Promise,则该表达式的值会被立即返回,不会造成等待。
3) async ==> 标识异步函数
- 当一个函数体内包含使用
await
的表达式时,这个函数必须用async
关键字声明。这样做会自动将该函数转换为异步函数,意味着它可以执行非阻塞的操作并返回一个 Promise 对象。 - 使用
async
定义的函数总是返回一个 Promise 对象,即使函数内部直接返回了一个普通值,该值也会被自动包装成一个已解决(fulfilled)的 Promise。
语法概览
-
定义一个异步函数:使用
async function
关键字来声明一个异步函数。async function fetchData() { // 异步操作... }
-
等待一个Promise:在异步函数内部,使用
await
来暂停执行直到 Promise 被解决(resolved)。注意,await
只能在异步函数内使用。const data = await fetchData();
实例演示
假设我们需要从API获取一些数据,并根据这些数据做进一步处理:
async function fetchAndProcessData() {
try {
let response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('Network response was not ok');
let data = await response.json(); // 解析JSON数据
console.log(data);
} catch (error) {
console.error('Failed to fetch data:', error);
}
}
fetchAndProcessData();
在这个例子中,我们首先调用 fetch
函数发起网络请求。由于 fetch
返回的是一个 Promise,我们可以直接使用 await
来等待这个 Promise 完成。如果请求成功,我们会进一步解析响应体中的 JSON 数据;如果请求失败或遇到异常,会通过 catch
块捕获并处理错误。
注意点
-
await
只能在async
函数中使用:尝试在非异步函数中使用await
将会导致语法错误。 -
错误处理:当使用
await
时,推荐使用try...catch
结构来捕获可能的异常。因为异步函数内的任何未捕获的错误都会导致返回的 Promise 被拒绝。 -
并发执行:如果你有多个独立的异步任务需要同时执行,可以考虑使用
Promise.all()
来等待所有任务完成。这样可以提高性能,因为它允许任务并行运行。async function getData() { let [data1, data2] = await Promise.all([fetchData1(), fetchData2()]); console.log(data1, data2); }
-
理解返回值:记住,
async
函数总是返回一个 Promise。即使你在函数中直接返回了一个普通值,它也会被自动包装进一个已解决的 Promise 中。 -
调试:由于
await
暂停了函数的执行,这可能会给调试带来一定的复杂性。确保你理解每个await
的位置以及它如何影响代码的执行流程。
明白了,我会将您最初提供的内容与详细的解释、示例和额外的信息整合在一起,以便为您提供一个全面的关于XMLHttpRequest
的指南。
XMLHttpRequest
XMLHttpRequest
(简称XHR)是一个用于在浏览器和服务器之间传输数据的JavaScript对象。它允许您发送请求并处理响应,而无需重新加载整个页面,从而实现动态网页内容更新。
概述
XMLHttpRequest
最早由微软引入,作为ActiveX对象的一部分,后来被其他浏览器厂商采纳并标准化,成为了现代Web开发中处理异步数据交换的核心技术之一。
主要属性与方法
-
创建XHR对象:
var xhr = new XMLHttpRequest();
-
open(method, url, async, user, password):初始化一个请求。
method
:HTTP方法(GET、POST等)。url
:请求的目标URL。async
:是否异步执行(通常是true
)。user
,password
:可选参数,用于认证。
-
send(body):发送请求到服务器。
- 对于GET请求,通常传递
null
或不传递参数。 - 对于POST请求,传递请求体数据。
- 对于GET请求,通常传递
-
setRequestHeader(header, value):设置HTTP请求头的值。
- 必须在
open()
之后但在send()
之前调用。
- 必须在
-
onreadystatechange:当
readyState
属性改变时触发的事件处理器。- 检查
xhr.readyState === 4 && xhr.status === 200
来确认请求成功完成。
- 检查
-
responseType:设置响应的数据类型(如
''
,'arraybuffer'
,'blob'
,'document'
,'json'
,'text'
)。 -
response:返回从服务器接收到的响应数据,格式由
responseType
决定。
基本模板步骤
- 创建
XMLHttpRequest
对象:这是发起请求的第一步。 - 调用
open
方法:初始化一个新的请求或重置一个现有的请求。 - 绑定事件监听器:通常是在
readystatechange
事件上设置回调函数来处理服务器响应。 - 调用
send
方法:发送请求到服务器。对于GET请求,参数通常是null
;对于POST请求,则需要传递数据。
完整示例
以下是一个按照上述步骤编写的完整示例,展示了如何使用XMLHttpRequest
发送一个GET请求并处理响应:
// 1. 创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
// 2. 使用 open 方法初始化请求
xhr.open('GET', 'https://api.example.com/data', true);
// 设置响应类型为 JSON(可选)
xhr.responseType = 'json';
// 3. 绑定事件监听器
xhr.onreadystatechange = function() {
// 检查请求是否完成并且成功
if (xhr.readyState === 4) { // readyState 4 表示请求已完成
if (xhr.status >= 200 && xhr.status < 300) { // 成功状态码范围
console.log('Response:', xhr.response);
} else {
console.error('Error: ', xhr.status, xhr.statusText);
}
}
};
// 可以在这里设置请求头(适用于 POST 请求等)
// xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
// 4. 发送请求
xhr.send(null); // 对于 GET 请求,send 参数通常是 null
特别的:xhr.status
和xhr.statusText
,它们是在处理XMLHttpRequest对象的响应时自动提供的属性,而不是需要您主动接受的参数
关键点总结
- 创建对象:通过
new XMLHttpRequest()
创建一个新的请求对象。 - 初始化请求:使用
open
方法设置HTTP方法、URL和其他选项。 - 设置事件监听器:在
onreadystatechange
中检查请求的状态,并根据状态码判断请求是否成功。 - 发送请求:使用
send
方法发送请求,对于POST请求,可以通过该方法传递请求体数据。
Axios
概述
Axios 是一个基于 Promise 的 HTTP 客户端,适用于浏览器和 Node.js 环境。它允许开发者通过简洁的 API 发起 HTTP 请求,并且支持拦截请求和响应、取消请求、自动转换 JSON 数据等特性 。
历史背景
Axios 诞生于需要更现代化的方式来处理网络请求的需求之中,特别是在 JavaScript 中使用 Promise 来简化异步编程的问题。在 Axios 出现之前,开发者通常依赖于 XMLHttpRequest 对象或者 jQuery 提供的 AJAX 方法来进行网络通信。然而,这些方法往往伴随着回调地狱的问题,而 Axios 通过提供基于 Promise 的接口解决了这一问题 。
创建一个实例
-
您可以使用自定义配置新建一个实例
-
axios.create([config])
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
请求配置
这些是创建请求时可以用的配置选项。只有 url
是必需的。如果没有指定 method
,请求将默认使用 GET
方法。
下面是一个简化的例子,仅保留了最常用的部分:
{
// `url` 是用于请求的服务器 URL
url: '/user',
// `method` 是创建请求时使用的方法,默认为 'get'
method: 'get',
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
baseURL: 'https://some-domain.com/api/',
// 自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` 是与请求一起发送的 URL 参数
params: {
ID: 12345
},
// `data` 是作为请求体被发送的数据,适用于 'PUT', 'POST', 'DELETE', 'PATCH' 请求方法
data: {
firstName: 'Fred'
},
// `timeout` 指定请求超时的毫秒数。
timeout: 1000,
// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false,
// `responseType` 表示浏览器将要响应的数据类型,默认为 'json'
responseType: 'json',
// `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。
validateStatus: function (status) {
return status >= 200 && status < 300;
}
}
**此外,如果您发现某些配置(如 headers
, baseURL
, timeout
)在多数请求中都是相同的,可以考虑创建一个 Axios 实例并设置默认配置,这样就不需要在每次请求时都指定它们。**例如:
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Requested-With': 'XMLHttpRequest'}
});
// 使用实例发起请求
instance.get('/user', {
params: {
ID: 12345
}
})
.then(response => console.log(response))
.catch(error => console.error(error));
响应结构
一个请求的响应包含以下信息。
{
// `data` 由服务器提供的响应
data: {},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 是服务器响应头
// 所有的 header 名称都是小写,而且可以使用方括号语法访问
// 例如: `response.headers['content-type']`
headers: {},
// `config` 是 `axios` 请求的配置信息
config: {},
// `request` 是生成此响应的请求
// 在node.js中它是最后一个ClientRequest实例 (in redirects),
// 在浏览器中则是 XMLHttpRequest 实例
request: {}
}
当使用 then
时,您将接收如下响应:
axios.get('/user/12345')
.then(function (response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
当使用 catch
,或者传递一个rejection callback
作为 then
的第二个参数时,响应可以通过 error
对象被使用,正如在错误处理部分解释的那样。
默认配置
您可以指定默认配置,它将作用于每个请求。
全局 axios 默认值
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
自定义实例默认值
// 创建实例时配置默认值
const instance = axios.create({
baseURL: 'https://api.example.com'
});
// 创建实例后修改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
配置的优先级
配置将会按优先级进行合并。它的顺序是:在lib/defaults.js中找到的库默认值,然后是实例的 defaults
属性,最后是请求的 config
参数。后面的优先级要高于前面的。
// 使用库提供的默认配置创建实例
// 此时超时配置的默认值是 `0`
const instance = axios.create();
// 重写库的超时默认值
// 现在,所有使用此实例的请求都将等待2.5秒,然后才会超时
instance.defaults.timeout = 2500;
// 重写此请求的超时时间,因为该请求需要很长时间
instance.get('/longRequest', {
timeout: 5000
});
拦截器
在请求或响应被 then 或 catch 处理前拦截它们。
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});
如果你稍后需要移除拦截器,可以这样:
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
可以给自定义的 axios 实例添加拦截器。
const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
错误处理
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// 请求已经成功发起,但没有收到响应
// `error.request` 在浏览器中是 XMLHttpRequest 的实例,
// 而在node.js中是 http.ClientRequest 的实例
console.log(error.request);
} else {
// 发送请求时出了点问题
console.log('Error', error.message);
}
console.log(error.config);
});
使用 validateStatus
配置选项,可以自定义抛出错误的 HTTP code。
axios.get('/user/12345', {
validateStatus: function (status) {
return status < 500; // 处理状态码小于500的情况
}
})
使用 toJSON
可以获取更多关于HTTP错误的信息。
axios.get('/user/12345')
.catch(function (error) {
console.log(error.toJSON());
});
组件封装
为了提高代码的可维护性和复用性,常常需要对 Axios 进行封装。封装可以包含设置默认配置(如 baseURL)、添加请求/响应拦截器等功能 。
创建 Axios 实例
首先,创建一个 Axios 实例并设置一些默认值:
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
请求拦截器
可以在请求发送前对请求进行处理,比如添加 token:
instance.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, error => Promise.reject(error));
响应拦截器
可以对响应数据进行统一处理,例如简化错误处理逻辑:
instance.interceptors.response.use(response => response.data, error => {
if (error.response && error.response.status === 401) {
// 处理未授权错误
}
return Promise.reject(error);
});
Axios 在 Vue.js 中的应用
在 Vue.js 项目中,可以通过创建一个专门的文件来封装 Axios 实例,并将其挂载到 Vue 实例上以便全局使用 。
// src/utils/request.js
import axios from 'axios';
const request = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
});
request.interceptors.request.use(config => {
// 在发送请求之前做些什么
return config;
}, error => {
// 对请求错误做些什么
return Promise.reject(error);
});
request.interceptors.response.use(response => {
// 对响应数据做点什么
return response.data;
}, error => {
// 对响应错误做点什么
return Promise.reject(error);
});
export default request;
// main.js
import Vue from 'vue';
import App from './App.vue';
import request from './utils/request';
Vue.prototype.$http = request;
new Vue({
render: h => h(App),
}).$mount('#app');
// 在组件内使用
this.$http.get('/user').then(response => {
console.log(response);
});
Fetch API
Fetch API 是一个现代的网络请求工具,它允许你通过 JavaScript 异步地请求资源,而不需要使用传统的 XMLHttpRequest 对象。Fetch API 提供了更强大和灵活的功能集,并且基于 Promise 实现。
基本用法
fetch(url)
发出 HTTP 请求,返回一个 Promise 对象。- 使用
.then()
或async/await
来处理请求结果。
fetch()
的功能与 XMLHttpRequest 基本相同,但有三个主要的差异。
(1)fetch()
使用 Promise,不使用回调函数,因此大大简化了写法,写起来更简洁。
(2)fetch()
采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象),更合理一些;相比之下,XMLHttpRequest 的 API 设计并不是很好,输入、输出、状态都在同一个接口管理,容易写出非常混乱的代码。
(3)fetch()
通过数据流(Stream 对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。XMLHTTPRequest 对象不支持数据流,所有的数据必须放在缓存里,不支持分块读取,必须等待全部拿到后,再一次性吐出来。
Response 对象:处理 HTTP 回应
Response 对象的同步属性
fetch()
请求成功以后,得到的是一个 Response 对象。它对应服务器的 HTTP 回应。
const response = await fetch(url);
前面说过,Response 包含的数据通过 Stream 接口异步读取,但是它还包含一些同步属性,对应 HTTP 回应的标头信息(Headers),可以立即读取。
async function fetchText() { let response = await fetch('/readme.txt'); console.log(response.status); console.log(response.statusText); }
标头信息属性有下面这些:
Response.ok
Response.ok
属性返回一个布尔值,表示请求是否成功,true
对应 HTTP 请求的状态码 200 到 299,false
对应其他的状态码。
Response.status
Response.status
属性返回一个数字,表示 HTTP 回应的状态码(例如200,表示成功请求)。
Response.statusText
Response.statusText
属性返回一个字符串,表示 HTTP 回应的状态信息(例如请求成功以后,服务器返回"OK")。
Response.url
Response.url
属性返回请求的 URL。如果 URL 存在跳转,该属性返回的是最终 URL。
Response.type
Response.type
属性返回请求的类型。可能的值如下:
basic
:普通请求,即同源请求。cors
:跨域请求。error
:网络错误,主要用于 Service Worker。opaque
:如果fetch()
请求的type
属性设为no-cors
,就会返回这个值,详见请求部分。表示发出的是简单的跨域请求,类似<form>
表单的那种跨域请求。opaqueredirect
:如果fetch()
请求的redirect
属性设为manual
,就会返回这个值,详见请求部分。
Response.redirected
Response.redirected
属性返回一个布尔值,表示请求是否发生过跳转。
判断请求是否成功
fetch()
发出请求以后,有一个很重要的注意点:只有网络错误,或者无法连接时,fetch()
才会报错,其他情况都不会报错,而是认为请求成功。
这就是说,即使服务器返回的状态码是 4xx 或 5xx,fetch()
也不会报错
- 只有通过
Response.status
属性,得到 HTTP 回应的真实状态码,才能判断请求是否成功。
async function fetchText() { let response = await fetch('/readme.txt'); if (response.status >= 200 && response.status < 300) { return await response.text(); } else { throw new Error(response.statusText); } }
- 另一种方法是判断
response.ok
是否为true
。
if (response.ok) { // 请求成功 } else { // 请求失败 }
读取内容的方法
Response
对象根据服务器返回的不同类型的数据,提供了不同的读取方法。
response.text()
:得到文本字符串。response.json()
:得到 JSON 对象。response.blob()
:得到二进制 Blob 对象。response.formData()
:得到 FormData 表单对象。response.arrayBuffer()
:得到二进制 ArrayBuffer 对象。
上面读取方法都是异步的,返回的都是 Promise 对象。必须等到异步操作结束,才能得到服务器返回的完整数据。
response.text()
- 用途:用于获取文本数据,比如 HTML 文件。
response.json()
- 用途:主要用于获取服务器返回的 JSON 数据。
response.formData()
- 用途:主要应用于 Service Worker 中,拦截用户提交的表单,允许修改某些数据后再提交给服务器。
response.blob()
- 用途:用于获取二进制文件,如图片、视频等。
response.arrayBuffer()
- 用途:主要用于处理流媒体文件,例如音频或视频文件的在线播放。
fetch()
的第二个参数:定制 HTTP 请求
fetch()
的第一个参数是 URL,还可以接受第二个参数,作为配置对象,定制发出的 HTTP 请求。
fetch(url, optionObj)
HTTP 请求的方法、标头、数据体都在这个对象里面设置。
(1)POST 请求
const response = await fetch(url, { method: 'POST', headers: { "Content-type": "application/x-www-form-urlencoded; charset=UTF-8", }, body: 'foo=bar&lorem=ipsum', }); const json = await response.json();
上面示例中,配置对象用到了三个属性。
method
:HTTP 请求的方法,POST
、DELETE
、PUT
都在这个属性设置。headers
:一个对象,用来定制 HTTP 请求的标头。body
:POST 请求的数据体。
(2)提交 JSON 数据
const user = { name: 'John', surname: 'Smith' }; const response = await fetch('/article/fetch/post/user', { method: 'POST', headers: { 'Content-Type': 'application/json;charset=utf-8' }, body: JSON.stringify(user) });
(3)提交表单
const form = document.querySelector('form'); const response = await fetch('/users', { method: 'POST', body: new FormData(form) })
fetch()
配置对象的完整 API
fetch()
第二个参数的完整 API 如下。
const response = fetch(url, { method: "GET", headers: { "Content-Type": "text/plain;charset=UTF-8" }, body: undefined, referrer: "about:client", referrerPolicy: "no-referrer-when-downgrade", mode: "cors", credentials: "same-origin", cache: "default", redirect: "follow", integrity: "", keepalive: false, signal: undefined });
fetch()
请求的底层用的是 Request() 对象的接口,参数完全一样,因此上面的 API 也是Request()
的 API。
这些属性里面,headers
、body
、method
前面已经给过示例了,下面是其他属性的介绍。
cache
cache
属性指定如何处理缓存。可能的取值如下:
default
:默认值,先在缓存里面寻找匹配的请求。no-store
:直接请求远程服务器,并且不更新缓存。reload
:直接请求远程服务器,并且更新缓存。no-cache
:将服务器资源跟本地缓存进行比较,有新的版本才使用服务器资源,否则使用缓存。force-cache
:缓存优先,只有不存在缓存的情况下,才请求远程服务器。only-if-cached
:只检查缓存,如果缓存里面不存在,将返回504错误。
mode
mode
属性指定请求的模式。可能的取值如下:
cors
:默认值,允许跨域请求。same-origin
:只允许同源请求。no-cors
:请求方法只限于 GET、POST 和 HEAD,并且只能使用有限的几个简单标头,不能添加跨域的复杂标头,相当于提交表单所能发出的请求。
credentials
credentials
属性指定是否发送 Cookie。可能的取值如下:
same-origin
:默认值,同源请求时发送 Cookie,跨域请求时不发送。include
:不管同源请求,还是跨域请求,一律发送 Cookie。omit
:一律不发送。
跨域请求发送 Cookie,需要将credentials
属性设为include
。
fetch('http://another.com', { credentials: "include" });
signal
signal
属性指定一个 AbortSignal 实例,用于取消fetch()
请求,详见下一节。
keepalive
keepalive
属性用于页面卸载时,告诉浏览器在后台保持连接,继续发送数据。
一个典型的场景就是,用户离开网页时,脚本向服务器提交一些用户行为的统计信息。这时,如果不用keepalive
属性,数据可能无法发送,因为浏览器已经把页面卸载了。
window.onunload = function() { fetch('/analytics', { method: 'POST', body: "statistics", keepalive: true }); };
redirect
redirect
属性指定 HTTP 跳转的处理方法。可能的取值如下:
follow
:默认值,fetch()
跟随 HTTP 跳转。error
:如果发生跳转,fetch()
就报错。manual
:fetch()
不跟随 HTTP 跳转,但是response.url
属性会指向新的 URL,response.redirected
属性会变为true
,由开发者自己决定后续如何处理跳转。
integrity
integrity
属性指定一个哈希值,用于检查 HTTP 回应传回的数据是否等于这个预先设定的哈希值。
比如,下载文件时,检查文件的 SHA-256 哈希值是否相符,确保没有被篡改。
fetch('http://site.com/file', { integrity: 'sha256-abcdef' });
referrer
referrer
属性用于设定fetch()
请求的referer
标头。
这个属性可以为任意字符串,也可以设为空字符串(即不发送referer
标头)。
fetch('/page', { referrer: '' });
referrerPolicy
referrerPolicy
属性用于设定Referer
标头的规则。可能的取值如下:
no-referrer-when-downgrade
:默认值,总是发送Referer
标头,除非从 HTTPS 页面请求 HTTP 资源时不发送。no-referrer
:不发送Referer
标头。origin
:Referer
标头只包含域名,不包含完整的路径。origin-when-cross-origin
:同源请求Referer
标头包含完整的路径,跨域请求只包含域名。same-origin
:跨域请求不发送Referer
,同源请求发送。strict-origin
:Referer
标头只包含域名,HTTPS 页面请求 HTTP 资源时不发送Referer
标头。strict-origin-when-cross-origin
:同源请求时Referer
标头包含完整路径,跨域请求时只包含域名,HTTPS 页面请求 HTTP 资源时不发送该标头。unsafe-url
:不管什么情况,总是发送Referer
标头。
Fetch
和Axios
选哪个?
选择使用 fetch
还是 axios
主要取决于你的具体需求、项目环境以及个人偏好。
优点:
- 内置支持:Fetch 是浏览器内置的,不需要额外引入库。
- 基于 Promise:提供了基于 Promise 的 API,使得异步操作更加直观易懂。
- 轻量级:没有额外的依赖,适合对包大小敏感的项目。
缺点:
- 不支持旧版浏览器:虽然现代浏览器都支持 fetch,但如果你需要兼容一些老旧版本的浏览器(如IE),可能需要引入polyfill。
- 默认不处理错误:对于 HTTP 错误状态码(如 404 或 500),fetch 不会拒绝 Promise,这可能会导致错误处理变得复杂。
- 功能相对简单:与 axios 相比,fetch 在处理某些高级特性时可能不如 axios 方便,例如自动转换 JSON 数据、取消请求等。
Axios
优点:
- 广泛的浏览器兼容性:Axios 支持所有主流浏览器,包括 IE。
- 更丰富的功能:提供了诸如拦截请求和响应、转换请求和响应数据、取消请求等功能。
- 更好的错误处理:Axios 对于 HTTP 错误状态码会自动拒绝 Promise,简化了错误处理逻辑。
- 易于使用的API:具有更加人性化和简洁的 API 设计,尤其是在处理复杂的请求配置时更为方便。
缺点:
- 增加项目体积:相对于原生的 fetch 来说,引入 axios 会增加项目的总体积。
- 需要额外安装:需要通过 npm 或者 yarn 等包管理工具进行安装,或者直接在 HTML 中通过
<script>
标签引入。