XHR 和 fetch API 都是浏览器给上层使用者暴露出来的 API,都是建立在http协议上的,给使用者提供了部分系统操作 http 包的能力。
XMLHttpRequest
XHR在1999年加入到 IE5 中,之后所有主流的浏览器都引入了该特性。AJAX架构的出现使XHR变得人尽皆知,XHR对象用于与服务器交互。可以用于获取任何类型的数据,甚至支持HTTP以外的协议(包括 file:// 和 FTP)。
原始的XHR使用方法
GET请求 👇
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.onload = function() {
console.log(xhr.response);
}
xhr.onerror = function() {
console.log('error');
}
xhr.send();
POST请求 👇
const convertToURL = function(obj) {
let result = '';
if(obj!== null && typeof obj === 'object') {
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
let tmp = `&${key}=${obj[key]}`;
result += tmp;
}
}
result = result.substring(1);
}
return result;
}
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = (res) => {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log('success response', xhr.response);
}
};
xhr.open('post', '/listByPage');
const params = {
pageNum: 1,
pageSize: 10
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.responseType = 'json';
xhr.send(convertToURL(params));
// 当设置为请求头的Content-Type设为application/json时,不能直接发送json对象,需要把json对象序列化才行。
// xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
// xhr.send(JSON.stringify(params));
XHR的属性
XHR.readyState == 状态(0,1,2,3,4),而且状态也是不可逆的:
- 0:请求未初始化,还没有调用 open()。
- 1:请求已经建立,但是还没有发送,还没有调用 send()。
- 2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
- 3:请求在处理中;通常响应中已有部分数据可用了,没有全部完成。
- 4:响应已完成;您可以获取并使用服务器的响应了。
XHR.onreadystatechange 用来监听 XHR.readyState 变化
XHR.response 相应实体,类型取决于responseType
XHR.responseType 定义响应类型的枚举值,'arraybuffer'、'blob'、'document'、'json'、'text'、''(默认)。
XHR.status 请求的响应状态
XHR.timeout 设置请求最大请求事件,超出该时间请求自动终止。
XHR.ontimeout 设置请求超时的回调。
XHR.upload 只读,代表上传进度。
XHR的方法
XHR.open() 初始化一个请求。
XHR.abort() 终止请求。
XHR.send() 发送请求。
XHR.setRequestHeader() 设置HTTP请求头。
XHR.getAllResponseHeaders() 获取所有CRLF 分隔的响应头,如没有收到响应则返回null。
XHR.getResponseHeader(key) 获取指定响应头的字符串,如果不存在该响应头或者响应未收到,则返回null。
XHR的事件
XHR.error 当请求被停止时触发,例如程序调用XHR.abort()时。也可以使用XHR.onabort属性
XHR.error 当请求遇到错误时触发,也可以使用XHR.onerror属性
XHR.load 当请求成功完成时触发,也可以使用XHR.onload属性
XHR.loadstart 当接收到响应数据时触发,也可以使用XHR.onloadstart属性
XHR.loadend 当结束时触发无论请求是成功(load)还是失败(abort或error),也可以使用XHR.onloadend属性
XHR.progress 当请求接收到更多数据时,周期性地触发,也可以使用XHR.onprogress属性
XHR.timeout 在预设时间内没有接收到响应时触发。也可以使用 ontimeout 属性
XHR的不足
从使用方法上看,http的request和response以及事件监听都在同一个xhr对象上,代码组织缺少语义化且可能造成回调地狱,手写一个xhr还是比较麻烦的。于是出现了jQuery.ajax() 和 axios 都是基于XHR使用Promise的方法设计的API,来避免回调的写法。
FETCH
fetch API 使用Promise语法结构。返回一个Promise,可以使用async/await,代码比原始的XHR清爽。
使用方法
GET请求
fetch(url)
.then( response => {
console.log(response.json());
})
.then( json => console.log(json) )
.catch( error => console.error('error:', error) );
POST请求
const url = '/listByPage';
const options = {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json;charset=UTF-8",
},
body: JSON.stringify({
pageNum: 1,
pageSize: 10
}),
};
fetch(url, options).then((response) => {
console.log(response.status);
});
fetch 可以接受两个参数input和init
fetch的input参数
它是一个 Request 对象或者是一个字符串,可以传一个 Request 实例
fetch的init参数
第二个参数可以接受一个对象。
method请求的方法,如GETPOST等headers请求头信息body请求的body信息,可以是Blob、BufferSource、FormData、URLSearchParams或者USVString对象- mode 请求的模式,如
cors、no-cors或者same-origin。 credentials: 请求的 credentials,如omit、``same-origin 或者include。为了在当前域名内自动发送 cookie ,必须提供这个选项。cache: 请求的 cache 模式:default、no-store、reload、no-cache、force-cache或者only-if-cached。redirect: 可用的 redirect 模式:follow(自动重定向),error(如果产生重定向将自动终止并且抛出一个错误), 或者manual(手动处理重定向)。referrer: 一个USVString可以是no-referrer、``client或一个 URL。默认是client。referrerPolicy: 指定了HTTP头部referer字段的值。可能为以下值之一:no-referrer、no-referrer-when-downgrade、origin、origin-when-cross-origin、unsafe-url 。integrity: 包括请求的 subresource integrity值。
fetch 的不足
默认不会发送cookies
默认无Cookie,需要设置init参数 credentials: 'same-origin'
请求错误不会reject
HTTP请求错误时,fetch返回的Promise不会被标记为reject,也不会执行catch。需要在resolve中判断response.ok是否为true。
fetch(url)
.then( response => {
if (response.ok) return response.json();
throw new Error('Network error');
})
.then( json => console.log(json) )
.catch( error => console.error('error:', error) );
没有timeout
fetch没有timeout配置,可以使用Promise.race()来设置超时。也可以通过signal参数来实现👇
Promise.race([
fetch('http://url', { method: 'GET' }),
new Promise(resolve => setTimeout(() => resolve('network error: timeout'), 3000))
])
.then(response => console.log(response))
没有abort
旧版的fetch一旦发起请求无法中指,现在支持 AbortController API 的浏览器可以通过调用abortController.abort()终止,Promise 会被标记为reject状态,同样可以实现 timeout 的效果。
const controller = new AbortController();
fetch(url, {
signal: controller.signal
})
.then( response => {
if (response.ok) return response.json();
throw new Error('Network error');
})
.then( json => console.log(json) )
.catch( error => console.error('error:', error) );
setTimeout(() => controller.abort(), 3000)
没有progress
不能显示文件上传和大型表单提交的进度
使用
很多网站的请求都包含fetch和xhr。
掘金:
bilibili
youtube
参考链接