浏览器完全支持,所以你仍然需要一个polyfill
。
八、fetch 的使用
一个基本的 fetch 请求:
const options = {
method: "POST", // 请求参数
headers: { "Content-Type": "application/json" }, // 设置请求头
body: JSON.stringify({ name: "123" }), // 请求参数
credentials: "same-origin", // cookie设置
mode: "cors" // 跨域
};
fetch("http://www.xxx.com")
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson); // 响应数据
})
.catch(function(err) {
console.log(err); // 异常处理
});
Fetch API
提供了一个全局的fetch()
方法,以及几个辅助对象来发起一个网络请求。
fetch()
方法用于发起获取资源的请求。它返回一个promise
,这个 promise
会在请求响应后被 resolve
,并传回 Response
对象。
可以通过Headers()
构造函数来创建一个你自己的headers
对象,相当于 response/request
的头信息,可以使你查询到这些头信息,或者针对不同的结果做不同的操作。
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
通过Request()
构造函数可以创建一个Request
对象,这个对象可以作为fetch
函数的第二个参数。
在fetch()
处理完promises
之后返回一个Response
实例,也可以手动创建一个Response
实例。
九、fetch polyfill 源码分析
由于fetch
是一个非常底层的API
,所以我们无法进一步的探究它的底层,但是我们可以借助它的polyfill
探究它的基本原理,并找出其中的坑点。
代码结构
由代码可见,polyfill
主要对Fetch
API 提供的四大对象进行了封装:
fetch 封装
代码非常清晰:
- 构造一个
Promise
对象并返回
- 创建一个
Request
对象
- 创建一个
XMLHttpRequest
对象
- 取出
Request
对象中的请求url
,请求方发,open
一个xhr
请求,并将Request
对象中存储的headers
取出赋给 xhr
xhr onload
后取出response
的status
、headers
、body
封装Response
对象,调用resolve
。
异常处理
可以发现,调用reject
有三种可能:
注意:当和服务器建立简介,并收到服务器的异常状态码如404、500
等并不能触发onerror
。当网络故障时或请求被阻止时,才会标记为 reject
,如跨域、url
不存在,网络异常等会触发onerror
。
所以使用 fetch 当接收到异常状态码都是会进入 then 而不是 catch。这些错误请求往往要手动处理。
可以在request
参数中传入signal
对象,并对signal
对象添加abort
事件监听,当xhr.readyState
变为4
(响应内容解析完成)后将 signal 对象的 abort 事件监听移除掉。
这表示,在一个fetch
请求结束之前可以调用signal.abort
将其终止。在浏览器中可以使用AbortController()
构造函数创建一个控制器,然后使用AbortController.signal
属性
这是一个实验中的功能,此功能某些浏览器尚在开发中
在 header 对象中维护了一个map
对象,构造函数中可以传入Header
对象、数组、普通对象类型的header
,并将所有的值维护到map
中。
之前在fetch
函数中看到调用了header
的forEach
方法,下面是它的实现:
可见header
的遍历即其内部map
的遍历。
另外Header
还提供了append、delete、get、set
等方法,都是对其内部的map
对象进行操作。
Request 对象
Request
对象接收的两个参数即fetch
函数接收的两个参数,第一个参数可以直接传递url
,也可以传递一个构造好的request
对象。第二个参数即控制不同配置的option
对象。
可以传入credentials、headers、method、mode、signal、referrer
等属性。
这里注意:
- 传入的
headers
被当作Headers
构造函数的参数来构造 header 对象。
cookie 处理
fetch 函数中还有如下的代码:
if (request.credentials === "include") {
xhr.withCredentials = true;
} else if (request.credentials === "omit") {
xhr.withCredentials = false;
}
默认的credentials
类型为same-origin
,即可携带同源请求的 coodkie。
然后我发现这里 polyfill 的实现和MDN-使用 Fetch以及很多资料是不一致的:
mdn: 默认情况下,fetch 不会从服务端发送或接收任何 cookies
于是我分别实验了下使用polyfill
和使用原生fetch
携带 cookie 的情况,发现在不设置credentials
的情况下居然都是默认携带同源cookie
的,这和文档的说明说不一致的,查阅了许多资料后都是说fetch
默认不会携带 cookie,下面是使用原生fetch
在浏览器进行请求的情况:
然后我发现在已经指出新版浏览器credentials
默认值已更改为same-origin
,旧版依然是omit
。
确实MDN-使用 Fetch这里的文档更新的有些不及时,误人子弟了…
Response 对象
Response
对象是fetch
调用成功后的返回值:
回顾下f
etch中对
Response`的操作:
xhr.onload = function() {
var options = {
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || "")
};
options.url =
"responseURL" in xhr
? xhr.responseURL
: options.headers.get("X-Request-URL");
var bo