FetchAPI介绍
面试的时候,经常会遇到要求应试者手写ajax,即使用原生JS代码自行构建XHR,然后绑上回调、传入参数,最后发送出去。
实际项目中,肯定不会使用原生XHR来发送ajax,因为写法还是功能都很简陋。工程中常见的几种ajax请求方式:使用jQuery框架的$.get( )
、$.ajax( )
等,或者使用专用的ajax库例如axios等。随着前端技术的发展,浏览器现在给我们提供了FetchAPI,用于以简单优雅的方式发起浏览器原生支持的ajax请求。
本篇文章参考了网上的一些博文和MDN,另外可以在这里查看Fetch的兼容性。注意FetchAPI的兼容性较差,IE全线不支持,甚至一些现代浏览器也只有新版本支持。
1. fetch( )方法介绍
fetch( )
方法由浏览器提供,它位于window
对象中。
它接受2个参数,第一个参数表示请求的地址,第二个参数传入配置,可省略。
它的结果是Promise,对于fetch( )的不同执行情况:
- 请求成功完成,触发
resolve
,将ajax取得的相应结果封装成一个Response
作参数传入之; - 请求出错,触发
reject
,产生一个错误。
1.1. 参数
fetch方法的签名如下:1
fetch(url [,init])
其中:url
参数表示请求资源的url,它也可以是一个Request对象;init
参数是一个可选的配置对象,它可以包含多条请求时的配置键值对。
init
参数是一个对象,它可以设置以下配置:
method
:请求发起的方式如POST
、GET
等,默认是GET
;headers
:请求头,是一个JSON或Header或URLSearchParams等,可以在这里控制请求头信息;body
:包含formdata
等body内容。GET和HEAD类型请求是没有body的;mode
:跨域资源访问控制,值一般为same-origin
或者cors
,此项一般不需改动;credentials
:是否一同提交cookie;cache
:资源缓存策略;redirect
:重定向处理策略;referrerPolicy
:重定向时候设置HTTP头;referrer
:URL重定向的来源配置;integrity
:验证资源的hash。
一般情况下,只需要指定method
;如果是Post请求,还可以指定body
项;如果要控制请求头则配置headers
,如果要发送cookie则配置credentials
。
如果显式指定了mode
的值为same-origin
,那么在跨域时请求将失败,未显式指定的时候是可以跨域请求的。
1.2. 详细配置
credentials
凭证参数:omit
:默认值,不会提交任何cookie;same-origin
:同源时才会提交cookie;include
:同源和跨域情况都提交cookie。
mode
模式:cors
:跨域模式,只暴露有限的头信息给服务器,但是body对服务器的开放的;cors-with-forced-preflight
:和cors类似,但是强制发起预检请求;same-origin
:仅同源,对于跨域请求会产生error;no-cors
:跨域但是不使用CORS流程的场合,例如CDN。此模式只能使用GET
、POST
、HEAD
类型,Header无法更改,且Response的type设为opaque
因此无法读取信息;navigate
:用于文档之间导航时创建导航请求时。
cache
资源缓存策略:default
:默认值,对于有效的缓存直接使用,缓存若过期则会验证是否改动,并在有改动的时候更新缓存;no-store
:不检查不更新缓存,直接下载资源;reload
:不检查缓存,直接下载资源并更新缓存;no-cache
:类似default
,但是即使缓存有效也会验证缓存;force-cache
:只要有资源就使用,不管过期与否;only-if-cache
:类似force-cache
,但没有缓存时会产生error。仅在mode为same-origin
时可用。
redirect
:重定向处理策略:follow
:前往重定向的url;error
:产生一个错误;manual
:手动处理重定向。
referrer
:URL重定向的来源配置:no-referrer
:不指定重定向来源url;client
:以浏览器上的url来作为来源地址;一个url字符串
:指定一个url作为重定向的来源地址。
integrity
验证参数:值形如sha1-xxxxxx...x
,表示哈希方法和结果值,用于验证资源是否正确。
1.3. 方法返回
如果请求成功,返回值是一个Response对象被作为参数传入resolve回调内。
例如:1
fetch('https://a-c.fun').then(t => { ... });
以上代码中省略号所在的作用域内,箭头函数的参数t就是获取的Response对象,可以对其进行访问等操作。
Response对象有以下几个参数:
url
:表示相应来源的url;useFinalURL
:为true或false表示是否是最终的url;body
:一个ReadableStream
类型的对象,无法直接读取;bodyUsed
:表示body是否被使用过,因为它只能被读取一次;headers
:请求所关联的Headers对象;ok
:为true或false表示请求成功与否;redirected
:是否来自重定向,可能是一个列表;status
:状态码,200表示成功的响应;statusText
:状态码描述字符串;type
:类型,可其中一个值:default
:通过构造函数构造出的Response;cors
:CORS跨域,body可读,header的可读性取决于CORS访问设定;basic
:同域响应,除了Set-Cookie
头其他都可用;opaque
:模式为no-cors
时的相应,数据、相应状态都受到严格限制;error
:网络错误,大部分参数都无法使用。
如果想要读取body里的数据,也就是响应的内容,可以使用一下几个方法:
.text( )
:以字符串方式处理,并构建一个Promise传入;.json( )
:以JSON方式来解析成对象,并构建一个Promise传入;.blob( )
:处理为Blob对象,它是无法更改的二进制对象,构建Promise传入;.arrayBuffer( )
:处理为ArrayBuffer对象并构建Promise传入;.formData( )
:处理为FormData对象,构建Promise并传入。
注意这里的几个方法全是返回一个Promise!
所以实际使用中一般会采用这种形式,例如获取API返回的文本:1
fetch('https://a-c.fun').then(res => res.text().then(t => {...}));
需要两个then才能获取到,上面代码中的省略号的作用域中,箭头函数的t参数就是传入的API返回的文本。
注意body只能被读取一次。
读取后bodyUsed会变为true,此时body便无法再被读取,因为它是一个ReadableStream类型。
如果想要重复使用body,可以使用以下代码:1
2
3
4let bak;
fetch('https://a-c.fun').then(res => {
bak = res.clone();
});
Response
对象有一个.clone( )
方法,可以在它未被读取前创建一个克隆对象,此时bak就是克隆的body了。
注意必须在未被读取前克隆,如果是读取后,那么该Response便不可读,也无法克隆。
1.4. 与XHR方式的区别
- fetch默认不提交Cookie,记得使用
credentials
,而XHR默认提交Cookie; - fetch不具备XHR的
abort( )
中断请求、不具备onprogress回调查看进度; - fetch在遇到4开头、5开头等状态码不会触发reject,而是抛出错误;
- fetch必须使用异步模式,不过可以使用await语法;
- fetch读取一次数据后便无法再读取,必须使用
clone( )
拷贝。
2. Response对象
fetch( )
在发送请求后返回一个Promise,而一个Response对象就被作为参数传入它的resolve
回调中。
除此之外,还可以使用以下方式来创建Response对象:
new Response(body [,init])
:构造函数来构建一个普通的Response对象:body
:可指定为FormData
、Blob
、URLSearchParams
等;init
:可选的配置参数,可以指定status
、statusText
、headers
;
Response.error( )
:工厂构造一个网络错误类型的Response对象;Response.redirect( )
:工厂构造一个重定向的Response对象。
3. Request对象
Request对象的构造方法的参数和fetch( )的参数相同,都是这样:1
new Request(url [,init])
它的第二个init配置参数还可以包含一些内容:context
:表示获取资源的类型,比如image
。在fetch( )使用时该值为fetch。
这些参数一般用于涉及到ServiceWorker的代码中。
fetch( )
的第一个参数可以直接传入一个Requset对象,此时将直接使用该Request的属性来发起请求。