Fetch API
浏览器实现的Fetch API 提供了一个获取资源的接口(包括跨域)
◉ 引言
- 它是什么?
- 为什么要使用它?
- 它能解决什么问题?
- 它在解决问题上有什么优势?
- 它有哪些劣势?
- 怎么使用它?
- 最佳实践和封装
1. 它是什么?
浏览器窗口对象 window 下挂载的 fetch() 方法用于发起获取资源的请求。它返回一个 Promise
,这个 Promise
会在请求响应后被resolve
,并传回 Response
对象。
类似于传统的Ajax
. 传统的Ajax
是指XMLHttpRequest
。
2. 为什么使用它?
Fetch API 是**基于 ES6 Promise 设计**,对 generator/yield,async/await 友好。
2.1 优点
- 语法简洁,语义清晰;
- 基于标准
Promise
实现,支持async/await
; - 不需要依赖第三方库,就可以优雅地使用
Ajax
; - 同构方便(基于
http
构建后端[node]fetch
);
2.2 缺点
- 新
API
的痛点:兼容性问题(polyfill
)后面细说; - 没有
process
; - 没有
cancel
或abort
; - 没有
timeout
;
3. 怎么使用它?
Fetch API 提供的一组对象,包括了 Headers, Request, Response, fetch
3.1 介绍
// XMLHttpRequest 发送异步请求
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => console.log(xhr.response)
xhr.onerror = error => console.error(error)
xhr.send()
---------------- 华丽的分割线 ----------------
// Fetch 发送异步请求
const myHeaders = new Headers()
// 添加请求头内容
myHeaders.append('Content-Type', 'text/xml')
const myRequest = new Request(url,{
headers: myHeaders,
// 或者字面量
// headers: {
// 'Content-Type': 'text/xml',
// },
})
// 请求
fetch(myRequest)
// 或者
// fetch(myRequest, {headers: myHeaders})
.then(response => response.json())
.then(result => console.log(result))
.catch(error => console.error(error))
// 或(在 async 函数中)
try {
let result = await fetch(myRequest)
console.log(result)
} catch (error) {
console.error(error)
}
先看一下 Fetch 原生支持率:
原生支持率并不高,引入下面这些polyfill
后可以完美支持 IE8+ :
-
由于
IE8
是ES3
,需要引入ES5
的polyfill
: es5-shim, es5-sham -
引入
Promise
的polyfill
: es6-promise -
引入
fetch
探测库:fetch-detector -
引入
fetch
的polyfill
: fetch-ie8 -
可选:如果你还使用了
jsonp
,引入 fetch-jsonp -
可选:开启
Babel
的runtime
模式,现在就使用async/await
Fetch polyfill
的基本原理是探测是否存在 window.fetch
方法,如果没有则用XHR
实现。这也是 github/fetch 的做法,但是有些浏览器(Chrome 45)原生支持Fetch
,但响应中有中文时会乱码,老外又不太关心这种问题,所以我自己才封装了 fetch-detector
和 fetch-ie8
只在浏览器稳定支持Fetch
情况下才使用原生Fetch
。
3.2 fetch
Promise<Response> fetch(input[, init])
/**
* @params { string|object } input
* @return { promise }
*/
fetch(input[, init])
3.2.1 input
此参数设置的为你想要访问的资源,可以是:
String
你想获取的资源地址Request
对象
3.2.2 init 可选参数
此参数包含了针对此次请求的自定义设置,包含以下设置:
- * method 请求类型,e.g. GET, POST;
- * headers 请求头参数,自定义对象或者
new Headers
; - * body 请求体,GET 和 HEAD 请求没有;
- * mode 请求模式,请求跨域的控制,可选参数:
cors
:该模式支持跨域请求,顾名思义它是以CORS
的形式跨域;当然该模式也可以同域请求不需要后端额外的CORS
支持;其对应的response type
为cors
。- cors 支持 三种
content-type
不支持application/json
(希望有同学分享下这个三个的区别)application/x-www-form-urlencoded
multipart/form-data
text/plain
- cors 支持 三种
no-cors
:该模式用于跨域请求但是服务器不带CORS
响应头,也就是服务端不支持CORS
;这也是fetch
的特殊跨域请求方式;其对应的response type
为opaque
。same-origin
:该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error
告知不能跨域;其对应的response type
为basic
。
- credentials 认证信息,请求是否包含认证信息,可选参数:
omit
忽略(默认)same-origin
同源请求带cookie
include
无论跨域还是同源请求都会带cookie
- cache 请求缓存模式设置,常用:
no-cache
; - redirect 请求被重定向的处理方式,常用:
follow
; - referrer
- referrerPolicy
- integrity
- keepalive
- * signal 值应为
AbortSignal
的实例,用来与请求通讯并可以用 AbortController 取消请求
示例
fetch('https://www.baidu.com', {
mode: 'cors',
referer: 'https://www.baidu.com'
}).then(res => console.log(res, res.text()))
3.2.3 fetch中的坑
Fetch
请求默认是不带cookie
的,需要设置fetch(url, {credentials: 'include'})
- 服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
3.3 Headers
3.3.1 构造函数
window.Headers,用于构建请求头内容,接口允许您对HTTP请求和响应头执行各种操作。 包括检索,设置,添加和删除。
const myHeaders = new Headers()
3.3.2 方法
- Headers.append()
给现有的header添加一个值, 或者添加一个未存在的header并赋值.
-
Headers.delete()
从Headers对象中删除指定header.
-
Headers.entries()
以
迭代器
的形式返回Headers对象中所有的键值对. -
Headers.get()
从Headers对象中返回指定header的第一个值.
-
Headers.getAll()
以数组的形式从Headers对象中返回指定header的全部值.
-
Headers.has()
以布尔值的形式从Headers对象中返回是否存在指定的header.
-
Headers.keys()
以
迭代器
的形式返回Headers对象中所有存在的header名. -
Headers.set()
替换现有的header的值, 或者添加一个未存在的header并赋值.
-
Headers.values()
以
迭代器
的形式返回Headers对象中所有存在的header的值.
const myHeaders = new Headers()
myHeaders.append('Content-Type', 'text/xml')
myHeaders.get('Content-Type')
// 返回 'text/xml'
3.4 Request
参考fetch部分
3.5 Response
3.5.1 构造函数
window.Response,fetch请求返回的数据都是Response的实例
const myResponse = new Response()
3.5.2 属性
- Response.type
包含Response的类型 (例如, basic
, cors
).
- Response.url
包含Response的URL.(可能被redirect后的地址)
- Response.useFinalURL
包含了一个布尔值来标示这是否是该Response的最终URL.
- Response.status
包含Response的状态码 (例如, 200 成功).
- Response.ok
包含了一个布尔值来标示该Response成功(状态码200-299) 还是失败.
- Response.redirected
表示该Response是否来自一个重定向,如果是的话,它的URL列表将会有多个
- Response.statusText
包含了与该Response状态码一致的状态信息 (例如, OK对应200).
- Response.headers
包含此Response所关联的 Headers 对象.
- Response.body
包含此Response所包含的内容.
- Body.bodyUsed
包含了一个布尔值来标示该Response是否读取过Body.
3.5.3 方法
-
Response.clone()
创建一个Response对象的克隆
-
Response.error()
返回一个绑定了网络错误的新的Response对象
-
Response.redirect()
用另一个URL创建一个新的 response.
Response 实现了Body 所以以下方法同样可用
-
Body.arrayBuffer()
读取 Response 对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为
ArrayBuffer
格式的promise对象 -
Body.blob()
读取 Response 对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为
Blob
格式的promise对象 -
Body.formData()
读取 Response 对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为
FormData
格式的promise对象 -
Body.json()
读取 Response 对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为
JSON
格式的promise对象 -
Body.text()
读取 Response 对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为
String
格式的promise对象,解析完整的 Response ,并返回一个String
对象的promise
4. 最佳实践和封装
将在代码中演示…