The Fetch API provides an interface for fetching resources (including across the network).
It will seem familiar to anyone who has used XMLHttpRequest,
but the new API provides a more powerful and flexible feature set.
- 编辑历史
- 其他
代码修改 2021-03-11
文章内容约2000字,推荐阅读时间15分钟。
Form 表单提交
前面提过了前后端分离开发,那么前后端交互自然成了开发过程一个重要的部分。
最基础的当然是使用 HTML 自带的功能,通过浏览器控制发送。
比如通过一个 Form 表单提交信息。
下面的 JSX 示例,点击后你填写的信息会提交到 /html/form.html
,同时页面也会跳转。
- HTML
- JSX live
<form action="/html/form.html">
<span>我们组成了一个表单!</span>
<br />
<br />
<span>写点什么吧!</span>
<input type="text" />
<br />
<br />
<input type="submit" value="点我提交!" />
</form>
Form 的表单提交是浏览器自身提供的功能,整个过程不需要别的代码控制,浏览器可以自动完成。
这种方式的缺点也是显而易见的:表单提交的行为会影响整个页面!
如果想要局部刷新页面,我们就需要用到 JavaScript 了。
AJAX 与 XHR
AJAX, which initially stood for Asynchronous JavaScript And XML, is a programming practice of building complex, dynamic webpages using a technology known as XMLHttpRequest.
简单来说,AJAX 是 JavaScript 的一部分,通过 XMLHttpRequest 的 API 来异步(或同步)更新 HTML 页面中的部分 DOM。
示例
例如下面的 JSX 示例,点击后通过 GET 获取 json 数据,更新指定的 div 的内容。
- JS
- JSX live
const xhr = new XMLHttpRequest();
const url = '/json/ajax.json'
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
document.getElementById("myDiv").innerHTML =
xhr.responseText;
}
};
xhr.open('GET', url, true);
xhr.send();
AJAX 一般的过程如下:
- 创建 XHR 对象
- 发送 XHR 请求
- 接受 XHR 响应
创建对象
一般情况,调用构造函数即可。
const xhr = new XMLHttpRequest();
兼容性
在旧版本的 IE 浏览器中,你可能需要以 ActiveXObject("Microsoft.XMLHTTP")
代替 XHR
发送请求
与发送有关的方法常用的如下:
方法 | 说明 |
---|---|
open(method, url, async) | 初始化一个请求。
|
setRequestHeader(header, value) | 设置HTTP请求头部。
|
send(body) | 发送HTTP请求。
|
abort() | 终止请求。 |
使用 AJAX 发送 GET POST 请求的示例如下:
- GET
- POST json
- POST form-data
- POST form-urlencoded
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.send();
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xmlhttp.setRequestHeader("Content-type", "application/json;charset=UTF-8");
xmlhttp.send(JSON.stringify(object));
const formData = new FormData();
formData.append('author', author);
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
// 发送 form-data 可能不需要手动设置请求头
// xmlhttp.setRequestHeader("Content-type", "application/form-data;charset=UTF-8");
xmlhttp.send(formData);
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send("foo=Foo&bar=Bar");
接受响应
与响应有关的属性常用的如下:
属性 | 说明 | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
onreadystatechange | 当readyState 改变时调用返回函数。 | ||||||||||||||||||
readyState | 表示 XHR 当前状态
| ||||||||||||||||||
status | 表示服务端返回的数字状态码。 | ||||||||||||||||||
response | 返回响应的正文。一般常用以下属性代替response :
|
- handle readyState changed
- readyState changed
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
console.log(xhr.responseText);
} else {
console.log(xhr.response);
}
} else {
console.log('XHR request has not been completed.');
}
};
const xhr = new XMLHttpRequest();
console.log('UNSENT', xhr.readyState); // readyState 为 0
xhr.open('GET', '/api', true);
console.log('OPENED', xhr.readyState); // readyState 为 1
xhr.onprogress = () => {
console.log('LOADING', xhr.readyState); // readyState 为 3
};
xhr.onload = () => {
console.log('DONE', xhr.readyState); // readyState 为 4
};
xhr.send(null);
其他
AJAX 也是缺点的,比如:动态更新页面会导致用户无法回溯到 JS 处理之前的页面状态,对搜索引擎支持较弱,也可能带来别的安全问题,等等······
但是一般情况下,AJAX 带来的性能和交互上的提升远远大于其缺点。
以 AJAX 为首的技术促进了前后端分离开发的发展,也成了现代前端的基础的一部分。
AJAX 的详细介绍可以参考 MDN 文档。
Fetch
The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest, but it provides a more powerful and flexible feature set. This article explains some of the basic concepts of the Fetch API.
从功能上来看,Fetch 与 AJAX 是相近的,并且设计和应用上更具有可扩展行和高效行,更加现代化。
Fetch 在 ES6 中添加,还是属于比较新的技术,部分浏览器的兼容性可能还不够完善,但通常来说,现代浏览器都可以完整使用。
Fetch 的最大优势在于其从设计上基于 Promise
,服务于前后端分离开发,更加贴近新的 JavaScript 标准。
使用 Fetch 不需要很高的学习成本,只要你理解了 XHR,就可以很快上手。
配合 ES6 的箭头函数和 ES7 的 async
/await
,Fetch 使用起来简便、优雅、高效。
示例
下面的示例与前面 AJAX 功能相同,但代码更加简洁。
使用 async
/await
,看上去就和同步代码一样。
- Fetch with async
- Fetch with then
- JSX live
const fetchGetData = async () => {
const response = await fetch('/json/fetch.json');
const data = await response.json();
console.log(data);
}
const fetchGetData = () => {
fetch('/json/fetch.json')
.then((response) => response.json())
.then((data) => {
console.log(data);
});
}
发送请求
请求方式
从实际来看,现在的前后端交互消息通常为 JSON 格式。
异步 POST 请求发送 JSON 格式,请求头为 application/json
;
若包含文件,则选择 application/form-data
。
- GET
- POST json
- POST form-data
- POST form-urlencoded
const fetchGetText = async () => {
const response = await fetch(url);
const text = await response.text();
console.log(text);
}
const fetchPostJson = asnyc () => {
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(object),
});
const data = await response.json();
console.log(data);
}
const fetchPostFormData = asnyc () => {
const formData = new FormData();
formData.append('author', author);
formData.append('file', file);
const response = fetch(url, {
method: 'POST',
body: formData,
});
const data = await response.json();
console.log(data);
}
const fetchPostForm = async () => {
const details = {
'author': 'IceyBlackTea',
'email': 'IceyBlackTea@outlook.com',
};
const formBody = [];
for (const property in details) {
const encodedKey = encodeURIComponent(property);
const encodedValue = encodeURIComponent(details[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body: formBody
});
const data = await response.json();
console.log(data);
}
异常处理
由于 Fetch 基于 Promise
,所以 Fetch 可以配合 async
/await
使用 .then()
与 .catch()
。
当然,如果你需要,也可以手动抛出异常,使用 try...catch
捕获。
- Error with try...catch
- JSX live
try {
const response = await fetch("/404");
if (!response.ok) {
throw Error(response.statusText);
}
const json = await response.json();
console.log(json);
} catch (error) {
alert(error);
};
- Error with .catch()
- JSX live
fetch("/404")
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
})
.catch((error) => {
alert(error);
})
.then((response) => response.json())
.catch((error) => {
alert(error);
});
其他
与 AJAX 相比,Fetch 还是有一些不同或者说缺陷的。
Fetch 对 错误的 HTTP 返回状态不会 reject。
如:404 状态码,Fetch 并不会报错;而只有网络状态导致请求不能完成时才会报错。 这导致:对错误的返回状态,你可能需要手动抛出异常。
Fetch 默认不会发送 cookies,你需要手动设置。
const fetchGetWithCookies = async () => {
const response = await fetch(url, {
method: 'GET',
credentials: 'include',
});
const data = await response.json();
console.log(data);
}
Fetch 不支持 abort,不支持超时控制
根据 MDN 文档的描述,该功能正在实现,或许马上就能使用了。
Fetch 的详细介绍可以参考 MDN 文档。
Fetch 作为比较新的 API,随着不断更新,兼容性已经逐渐完善,功能也日益丰富。
我个人非常期待 Fetch 替代 AJAX,成为异步获取资源的首选。
最后
本篇用了很多 docusaurus v2
的新功能,希望能使文章内容更加易读易用。
希望你能喜欢。
缺陷也很明显,写起来已经不能是纯 MD 了,必须添加许多 JSX 代码······
这篇将近 800 行了,着实有点累😥。
如果有其他问题,可以在我的 github.io 项目的 issue 留言,欢迎提问~
感谢观看!Thank you for watching!