一文搞懂前端请求XHR,AJAX,Fetch和Axios
在前端开发中,网络请求是必不可少的技能之一。网络请求可以让我们与服务器交换数据,实现各种功能和效果。但是,你知道JavaScript中有哪些网络请求方法吗?你知道它们的区别和优劣吗?你知道如何根据不同的场景选择合适的网络请求方法吗?本文将为你搞懂前端请求的奥秘。
什么是XHR?
XMLHttpRequest
(XHR)是一个 JavaScript 对象,它可以用来向服务器发送 HTTP 请求,从而实现网页的局部更新。是 AJAX 编程的核心技术。
XMLHttpRequest包含的一些属性
属性名称 | 属性作用 | 备注 |
---|---|---|
onreadystatechange | 事件处理器。 | 当 readyState 属性发生变化时调用 |
readyState 只读 | 请求的状态码。 | 返回 一个无符号短整型(unsigned short)数字(0,1,2,3,4) |
response 只读 | 包含整个响应实体(response entity body)。 | 返回一个 ArrayBuffer、Blob、Document,或 DOMString,具体是哪种类型取决于 XMLHttpRequest.responseType 的值。 |
responseText 只读 | 返回一个 DOMString | 该 DOMString 包含对请求的响应,如果请求未成功或尚未发送,则返回 null。 |
responseType | 一个用于定义响应类型的枚举值(enumerated value)。 | 它包含的值有ArrayBuffer,Blob,Document,json,text |
responseURL 只读 | 返回经过序列化(serialized)的响应 URL | 如果该 URL 为空,则返回空字符串。 |
responseXML 只读 | 返回一个 Document | 其中包含该请求的响应,如果请求未成功、尚未发送或是不能被解析为 XML 或 HTML,则返回 null。 |
status 只读 | 请求的响应状态。 | status 码是标准的 HTTP status codes |
upload | 用来表示上传的进度 | 返回一个 XMLHttpRequestUpload对象 |
timeout | 设置超时时间 | xhr.timeout = 2000; // 超时时间,单位是毫秒 |
withCredentials | 用来指定跨域 Access-Control 请求是否应当带有授权信息 | 如 cookie 或授权 header 头。除非在发送 XMLHttpRequest 请求之前,将 withCredentials 设置为 true ,否则来自不同域的 XMLHttpRequest 响应无法为自己的域设置 cookie 值 |
XMLHttpRequest.readyState 属性返回一个 XMLHttpRequest 代理当前所处的状态。一个 XHR 代理总是处于下列状态中的一个
值 | 状态 | 描述 |
---|---|---|
0 | UNSENT | 代理被创建,但尚未调用 open() 方法。 |
1 | OPENED | open() 方法已经被调用。 |
2 | HEADERS_RECEIVED | send() 方法已经被调用,并且头部和状态已经可获得。 |
3 | LOADING | 下载中;responseText 属性已经包含部分数据。 |
4 | DONE | 下载操作已完成。 |
实例方法
var HttpRequest = new XMLHttpRequest()
创建请求
HttpRequest.open(method, url, async, user, password);
method:HTTP 方法,比如
GET
、POST
、PUT
、DELETE
、等url:发送请求的 URL
async:可选,布尔参数,默认为true,如果值为
false
,send()
方法直到收到答复前不会返回。user:可选,用户名用于认证用途;默认为 null。
password :可选,密码用于认证用途,默认为 null。
发送请求
HttpRequest.send(body)
body,可选参数,在 XHR 请求中要发送的数据体
例如:HttpRequest.send("foo=bar&lorem=ipsum");
终止请求
HttpRequest.abort();
终止该请求。当一个请求被终止,它的 readyState 将被置为 XMLHttpRequest.UNSENT (0),并且请求的 status 置为 0。
指定类型
overrideMimeType() 方法必须在 send() 方法之前调用
HttpRequest.overrideMimeType(mimeType)
例如:HttpRequest.overrideMimeType("text/plain");
一些常见的 MIME 类型:text/html,text/plain,text/css,text/javascript,application/json,application/xml,image/jpeg,image/png
设置HTTP响应头部的值
setRequestHeader() 方法必须在 open() 方法和 send() 方法之间调用。
HttpRequest.setRequestHeader(header, value);
例如:设置 Content-Type 的值为 application/json
HttpRequest.setRequestHeader("Content-Type", "application/json");
常见的HTTP响应头部
- Accept:指定客户端能够接收的内容类型。
- Accept-Charset:指定客户端能够接收的字符集。
- Accept-Encoding:指定客户端能够接收的内容编码。
- Accept-Language:指定客户端能够接收的语言。
- Authorization:包含客户端提供给服务器以验证自己身份的凭证。
- Cache-Control:指定请求和响应遵循的缓存机制。
- Connection:指定与连接相关的选项。
- Content-Length:指定请求体的长度,以字节为单位。
- Content-Type:指定请求体的 MIME 类型。
- Cookie:包含来自客户端的 Cookie 值。
- Host:指定被请求资源的主机名和端口号。
- If-Modified-Since:允许在请求中包含条件,只有请求内容在指定日期后或之后修改过才返回它。
- Referer:包含当前页面的 URL,用于标识请求是从哪个页面发送过来的。
- User-Agent:包含发起请求的用户代理。
获取响应头
获取指定响应头文本的字符串
var myHeader = HttpRequest.getResponseHeader(name);
//name一个字符串,表示要返回的报文项名称。
//例如:
var contentType=HttpRequest.getResponseHeader("Content-Type"));
获取所有响应头
var headers = HttpRequest.getAllResponseHeaders();
返回结果:每一行通过\r\n 来进行分割。
date: Fri, 08 Dec 2023 21:04:30 GMT\r\n content-encoding: gzip\r\n x-content-type-options: nosniff\r\n server: meinheld/0.6.1\r\n x-frame-options: DENY\r\n content-type: text/html; charset=utf-8\r\n connection: keep-alive\r\n strict-transport-security: max-age=63072000\r\n vary: Cookie, Accept-Encoding\r\n content-length: 6502\r\n x-xss-protection: 1; mode=block\r\n
其他
返回所有的响应头
var headers = HttpRequest.getAllResponseHeaders();
事件
说明
loadstart:用于指示请求已经开始加载数据,可以使用 loadstart 事件来检测请求何时开始加载数据。
load:当一个XMLHttpRequest请求完成的时候会触发load 事件。
loadend:在一个资源的加载进度停止之后被触发 (例如,在已经触发“error”,“abort”或“load”事件之后)
progress:progress 事件用于返回进度信息,在请求接收到更多数据时定期触发。
error:当请求遇到错误时,将触发error
事件。
abort:当一个请求终止时 abort 事件被触发,比如程序执行 XMLHttpRequest.abort()。
示例
<div class="controls">
<input class="xhr success" type="button" name="xhr" value="Click to start XHR (success)" />
<input class="xhr error" type="button" name="xhr" value="Click to start XHR (error)" />
<input class="xhr abort" type="button" name="xhr" value="Click to start XHR (abort)" />
</div>
<textarea readonly class="event-log"></textarea>
const xhrButtonSuccess = document.querySelector('.xhr.success');
const xhrButtonError = document.querySelector('.xhr.error');
const xhrButtonAbort = document.querySelector('.xhr.abort');
const log = document.querySelector('.event-log');
function handleEvent(e) {
log.textContent = log.textContent + `${e.type}: ${e.loaded} bytes transferred\n`;
}
function addListeners(xhr) {
xhr.addEventListener('loadstart', handleEvent);
xhr.addEventListener('load', handleEvent);
xhr.addEventListener('loadend', handleEvent);
xhr.addEventListener('progress', handleEvent);
xhr.addEventListener('error', handleEvent);
xhr.addEventListener('abort', handleEvent);
}
function runXHR(url) {
log.textContent = '';
const xhr = new XMLHttpRequest();
addListeners(xhr);
xhr.open("GET", url);
xhr.send();
return xhr;
}
xhrButtonSuccess.addEventListener('click', () => {
runXHR('dgszyjnxcaipwzy.jpg');
});
xhrButtonError.addEventListener('click', () => {
runXHR('https://somewhere.org/i-dont-exist');
});
xhrButtonAbort.addEventListener('click', () => {
runXHR('dgszyjnxcaipwzy.jpg').abort();
});
什么是AJAX?
AJAX 代表异步的 JavaScript 和 XML(Asynchronous JavaScript And XML)。简单点说,就是使用 XMLHttpRequest
(XHR)对象与服务器通信。
Ajax 本身不是一种编程语言,而是一种利用以下技术的组合。
- 浏览器内置的XMLHttpRequest对象(向服务器请求数据)
- JavaScript和HTML DOM(显示或使用数据)
- XML、JSON或纯文本(作为数据传输格式)
Ajax 中的 X 代表 XML,但是 JSON 才是首选,因为它更加轻量
AJAX的工作原理
- 在网页中发生一个事件(页面加载,按钮被点击等)
- JavaScript创建一个XMLHttpRequest对象
- XMLHttpRequest对象向服务器发送一个请求
- 服务器处理请求
- 服务器向网页返回一个响应
- JavaScript读取响应
- JavaScript执行相应的操作(如更新网页)
AJAX示例
创建一个XMLHttpRequest对象,用来向服务器发送http请求
const httpRequest = new XMLHttpRequest();
定义一个回调函数,当请求状态发生变化时触发
httpRequest.onreadystatechange = function() {
//判断请求是否完成(readyState为4)并且成功(status为200)
if (httpRequest.readyState === 4 && httpRequest.status === 200) {
//在这里处理返回的数据,比如显示在网页上
console.log(httpRequest.responseText);
}
};
建立一个GET请求,指定请求的URL
httpRequest.open("GET", "https://example.com/api/data");
发送请求
httpRequest.send();
Ajax的优缺点
优点:
- 减少页面刷新:
Ajax
可以在页面不刷新的情况下获取和显示数据,减少了用户等待时间和流量消耗。 - 提高用户体验:由于
Ajax
可以实现异步请求和响应,使得用户可以在不中断操作的情况下获取数据,从而提高了用户的体验。 - 减轻服务器压力:由于
Ajax
可以部分更新页面,减少了服务器处理请求的次数,从而减轻了服务器的压力。 - 支持多种数据格式:
Ajax
可以支持多种数据格式,如XML
、JSON
等,使得数据的传输和处理更加灵活。
缺点:
- 对
SEO
不友好:由于Ajax
的异步请求不会刷新整个页面,搜索引擎很难获取Ajax
加载的数据,从而降低了网站的SEO
优化效果。 - 安全性问题:
Ajax
可能会导致跨站点脚本攻击(Cross-site scripting, XSS)
和跨站点请求伪造(Cross-site request forgery, CSRF)
等安全问题,开发人员需要采取相应的安全措施。
什么是Fetch?
Fetch 是 XMLHttpRequest 的升级版,用于在 JavaScript 脚本里面发出 HTTP 请求。
Fetch的用法
基本语法
let promise = fetch(url, [options])
url
—— 要访问的 URL。options
—— 可选参数:method,header 等。
//典型的 fetch 请求由两个 await 调用组成
let response = await fetch(url, options); // 解析 response header
let result = await response.json(); // 将 body 读取为 json
//或者以promise的形式
fetch(url, options)
.then(response => response.json())
.then(result => /* process result */)
Headers
Headers对象表示HTTP请求或响应的头部信息。我们可以使用Headers对象来设置请求或响应的头部信息,比如Content-Type、Authorization等。
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
});
fetch('https://api.example.com/data', {
headers: headers
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
它包含的一些实例方法
Headers.append()
:给现有的 header 添加一个值,或者添加一个未存在的 header 并赋值。
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', 'Bearer my-token');
Headers.delete()
:从 Headers 对象中删除指定 header.
Headers.entries()
:以 迭代器
的形式返回 Headers 对象中所有的键值对。
var myHeaders = new Headers();
myHeaders.append('Content-Type', 'text/xml');
myHeaders.append('Vary', 'Accept-Language');
for (var pair of myHeaders.entries()) {
console.log(pair[0]+ ': '+ pair[1]);
}
//输出结果
content-type: text/xml
VM186:6 vary: Accept-Language
Headers.get()
:以 ByteString
的形式从 Headers 对象中返回指定 header 的全部值。
console.log(headers.get('content-type')); // 输出: "application/json"
Headers.has()
:以布尔值的形式从 Headers 对象中返回是否存在指定的 header.
Headers.keys()
:以迭代器
的形式返回 Headers 对象中所有存在的 header 名。
Headers.set()
:替换现有的 header 的值,或者添加一个未存在的 header 并赋值。
Headers.values()
:以迭代器
的形式返回 Headers 对象中所有存在的 header 的值。
Request
Request对象表示一个HTTP请求。我们可以使用Request对象来设置请求选项,比如请求URL、请求方法、请求头等。下面是Request对象的使用示例:
const request = new Request('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify({ name: 'John', age: 30 })
});
fetch(request)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
它的部分属性
属性 | 说明 |
---|---|
Request.method | 包含请求的方法 (GET, POST, 等.) |
Request.url | 包含这个请求的 URL。 |
Request.headers | 包含请求相关的Headers对象。 |
Request.context | 包含请求的上下文 (例如:audio, image, iframe, 等) |
Body.body | body 类型可以是一个Blob ,BufferSource , FormData , URLSearchParams , USVString 或者ReadableStream 类型 |
Response
fetch()
请求成功以后,得到的是一个 Response 对象。它对应服务器的 HTTP 回应。我们可以使用Response对象来获取响应的信息,比如状态码、响应头、响应体等
fetch('https://api.example.com/data')
.then(response => {
console.log(response.status);
console.log(response.headers.get('Content-Type'));
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error(error));
可以看到返回的有这些信息
属性 | 说明 |
---|---|
Response.ok | 返回一个布尔值,表示请求是否成功,true对应 HTTP 请求的状态码 200 到 299,false对应其他的状态码。 |
Response.status | 返回一个数字,表示 HTTP 回应的状态码(例如200,表示成功请求)。 |
Response.statusText | 返回一个字符串,表示 HTTP 回应的状态信息(例如请求成功以后,服务器返回"OK")。 |
Response.url | 返回请求的 URL。如果 URL 存在跳转,该属性返回的是最终 URL。 |
Response.type | 包含一种响应的类型,它的值有basic,cors,error,opaque,opaqueredirect。 |
Response.redirected | 返回一个布尔值,表示请求是否发生过跳转。 |
Response.type
basic
:普通请求,即同源请求。cors
:跨域请求。error
:网络错误,主要用于 Service Worker。opaque
:如果fetch()
请求的type
属性设为no-cors
,就会返回这个值,详见请求部分。表示发出的是简单的跨域请求,类似<form>
表单的那种跨域请求。opaqueredirect
:如果fetch()
请求的redirect
属性设为manual
,就会返回这个值,详见请求部分。
使用示例
发送请求
//GET请求
fetch('https://api.example.com/data')
.then(response => console.log(response))
.catch(error => console.error(error));
//POST请求
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'axjy', password: '123456' })
};
fetch('https://api.example.com/login', requestOptions)
.then(response => console.log(response))
.catch(error => console.error(error));
处理响应数据
处理响应数据,可以使用 Response对象
提供的方法。
fetch('https://example.com/data.json')
.then(response => response.json())//使用`.json()`方法将响应正文解析为 `JSON格式` 的数据
.then(data => console.log(data))
.catch(error => console.error(error)); //处理错误,如果请求失败,catch() 方法将被调用
处理HTTP错误
如果服务器返回HTTP错误状态代码(例如404或500),fetch()
方法不会引发错误。它会将响应对象传递给 then()
方法。所以可以使用 Response
对象的属性来确定响应的状态代码,并相应地处理响应。
fetch('https://example.com/data.json')
.then(response => {
//可以根据response.ok,response.status来错误http错误的状态码
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error(error));
Fetch的优缺点
优点
- 支持跨域请求:在配置中,添加mode: 'no-cors'就可以跨域了
fetch('/users.json', {
method: 'post',
mode: 'no-cors',
data: {}
}).then(function() { /* handle response */ });
- 可以自定义请求头,更好地控制请求。
缺点
-
fetch
只对网络请求报错,对400
,500
都当做成功的请求,需要封装去处理 -
fetch
没有办法原生监测请求的进度,而XHR
可以。
基于 fetch 封装的库
redaxios,轻量级的 Axios 替代品,可以在浏览器和Node.js环境中使用。适合那些想要使用 Axios API 但又不想引入额外依赖的场景。
umi-request,是一个轻量级的 http 请求库,它兼具 fetch 和 axios 的特点,适合在 umi 框架中使用
什么是Axios?
Axios是一个基于promise网络请求库,在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。
特性
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
Axios的使用
基本使用
Axios文档已经写的很清楚了
取消请求
从 v0.22.0
开始,Axios 支持以 fetch API 方式—— AbortController
取消请求:
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// 取消请求
controller.abort()
请求体编码
默认情况下,axios将 JavaScript 对象序列化为 JSON
。 要以application/x-www-form-urlencoded
格式发送数据,可以使用qs
库编码数据。
const qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 }));
Axios 优缺点
优点:
Promise API
:Axios使用Promise API
,因此可以轻松处理异步操作。Promise API
具有更清晰的语法和更好的可读性,因为它们允许在异步操作完成之前进行链式调用,从而避免了回调地狱问题。- 简单易用:
Axios
的API设计简单且易于使用,而且它还提供了许多可用的配置选项,例如设置请求头、超时时间、认证等等,让开发者可以更轻松地定制请求。 - 可扩展性:
Axios
可以通过添加拦截器(interceptors
)来实现许多自定义功能,例如添加请求拦截器、响应拦截器和错误拦截器等等。这些拦截器可以让开发者在请求和响应过程中进行自定义操作。
缺点:
- 可能出现跨域问题:
Axios
不能直接解决跨域请求的问题。尽管Axios
可以设置跨域请求头,但是它不能绕过浏览器的安全限制。这意味着在某些情况下,开发者可能需要通过其他方式来解决跨域请求的问题。
总结
XHR:是一个 JavaScript 对象
ajax:是一个技术统称,XMLHttpRequest 是实现 Ajax 的一种方式。
fetch:是一个API,es6新增的用于网络请求标准api。
axios:是一个库,用于网络请求的第三方库。
参考资料:
Ajax - Web 开发者指南 | MDN (mozilla.org)
🎨【点赞】【关注】不迷路,更多前端干货等你解锁
往期推荐
转载自:https://juejin.cn/post/7243276385895497787