关于Ajax,一个最让人诧异的秘密就是,XMLHttpRequest
的底层API其实并不是为现在普遍流行的做法设计的,它本是用来做其它事情的。虽然XMLHttpRequest
目前为止提供的API还是非常好用的,但其实它可以做的更好。而这更好的API已经诞生了,它就是fetch
方法。下面我们来看看基本的window.fetch
的用法。最新版的火狐浏览器和谷歌浏览器都提供了对这个API的支持。
整体看来,XMLHttpRequest
过于复杂,而且它的名字有些困惑,为什么“XML”全是大写,而”Http”采用驼峰式命名方法?不管怎样,XMLHttpRequest
的用法是这样的:
// Just getting XHR is a mess! if (window.XMLHttpRequest) { // Mozilla, Safari, ... request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} } } // Open, send. request.open('GET', 'http://www.webhek.com/ajax-endpoint', true); request.send(null);
当然,有很多的JavaScript框架将XMLHttpRequest
封装的非常方便,但其内部都是上面这个简单例子里的乱糟糟写法。
fetch
用法 fetch
方法是全局对象window
里提供的,它的第一个参数就是URL地址:
// url (required), options (optional) fetch('/some/url', { method: 'get' }).then(function(response) { }).catch(function(err) { // Error :( });
它很像我们之前介绍的电池状态信息API,这个fetch API使用promises处理结果results/callbacks:
// Simple response handling fetch('/some/url').then(function(response) { }).catch(function(err) { // Error :( });; // Chaining for more "advanced" handling fetch('/some/url').then(function(response) { return //... }).then(function(returnedValue) { // ... }).catch(function(err) { // Error :( });;
如果你以前没有用过then
类似的方法,那就学着使用它吧——很快到处都会出现这种方法。
假如说,你需求发起一个JSON请求 — 返回的数据对象里有一个json
方法,能将原始数据转化成JavaScript对象:
fetch('http://www.webhek.com/demo/arsenal.json').then(function(response) { // Convert to JSON return response.json(); }).then(function(j) { // Yay, `j` is a JavaScript object console.log(j); });
显然,它就是一个简单的JSON.parse(jsonString)
调用,但json
方法做为简写方式还是很方便的。
JSON并不是我们在任何时候都需要的数据格式,下面介绍一下如何处理HTML或文本格式的响应:
fetch('/next/page')
.then(function(response) {
return response.text();
}).then(function(text) {
//
非常相似,你可以在Promise的then
方法提供的response对象上使用text()
。
头信息和元信息(Metadata)
response
对象里还包含了丰富的HTTP头信息和metadata信息,你可以直接用get
方法获取它们:
fetch('http://www.webhek.com/demo/arsenal.json').then(function(response) {
// Basic response properties, available at any time
console.log(response.status)
console.log(response.statusText)
// Grabbing response headers via `get`
console.log(response.headers.get('Content-Type'))
console.log(response.headers.get('Date'))
})
你还可以在调用请求过程中set这些头信息:
// url (required), options (optional)
fetch('/some/url', {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
提交表单信息
Ajax另外一个常用的地方是发送表单数据,下面的这个例子就是如何用fetch
发送表单数据:
fetch('/submit', {
method: 'post',
body: new FormData(document.getElementById('comment-form'))
});
如果你发送的数据是JSON格式:
fetch('/submit-json', {
method: 'post',
body: JSON.stringify({
email: document.getElementById('email')
answer: document.getElementById('answer')
})
});
非常容易,而且看起来也非常顺眼!
其它要说明的
fetch
是一个更好用的API,但它目前还没有提供取消请求的方法,所以,程序员用起来也需要考量一下。
这个新fetch
API相比起XMLHttpRequest
更简单,更易读,是很好的Ajax替代方法;fetch
有很明显的优势,相信很快会流行起来!
================================
fetch polyfill
pollyfill 下载
A window.fetch JavaScript polyfill. — More...
http://github.github.io/fetch/
Fetch 是 window.fetch 的 JavaScript polyfill。
全局 fetch
函数是 web 请求和处理响应的简单方式,不使用 XMLHttpRequest。这个 polyfill 编写的接近标准的 Fetch 规范(https://fetch.spec.whatwg.org)。
fetch 函数支持所有的 HTTP 方式:
HTML:
fetch('/users.html')
.then(function(response) {
return response.text()
}).then(function(body) {
document.body.innerHTML = body
})
JSON:
fetch('/users.json')
.then(function(response) {
return response.json()
}).then(function(json) {
console.log('parsed json', json)
}).catch(function(ex) {
console.log('parsing failed', ex)
})
===========================
fetch API 详解 :
多年来,XMLHttpRequest
一直是web开发者的亲密助手。无论是直接的,还是间接的, 当我们谈及Ajax技术的时候,通常意思就是基于XMLHttpRequest
的Ajax,它是一种能够有效改进页面通信的技术。 Ajax的兴起是由于Google的Gmail所带动的,随后被广泛的应用到众多的Web产品(应用)中,可以认为, 开发者已经默认将XMLHttpRequest
作为了当前Web应用与远程资源进行通信的基础。 而本文将要介绍的内容则是XMLHttpRequest
的最新替代技术——Fetch API, 它是W3C的正式标准,本文将会介绍Fetch API的相关知识,以及探讨它所能使用的场景和能解决的问题。
Statement
原文地址: http://www.sitepoint.com/introduction-to-the-fetch-api/
译者:景庄,Web开发工程师,主要关注于前端工程化技术、Node.js、React等。
Fetch API
Fetch API提供了一个fetch()
方法,它被定义在BOM的window
对象中,你可以用它来发起对远程资源的请求。 该方法返回的是一个Promise对象,让你能够对请求的返回结果进行检索。
为了能够进一步的解释Fetch API,下面我们写一些代码来具体的介绍它的用法: 下面这个例子将会通过Flicker API来检索一些图片,并将结果插入到页面中。到目前为止, Fetch API还未被所有的浏览器支持。因此,如果你想体验这一技术,最好使用最新版本的Chrome浏览器。 为了能够正确的调用Flicker API,你需要申请自己的API KEY,将其插入到代码中的适当位置,即your_api_key
那个位置。
来看看第一个任务:我们使用API来从Flicker中检索一些有关”企鹅“的照片,并将它们展示在也没中,代码如下。
1
2
3
4
5
6
7
8
9
10
11
12
/* API URL, you need to supply your API key */
var URL = 'https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=your_api_key&format=json&nojsoncallback=1&tags=penguins' ;
function fetchDemo ( ) {
fetch ( URL ) . then ( function ( response ) {
return response . json ( ) ;
} ) . then ( function ( json ) {
insertPhotos ( json ) ;
} ) ;
}
fetchDemo ( ) ;
上面的代码看起来很简单:首先是构造请求的URL,然后将URL传递给全局的fetch()
方法,它会立刻返回一个Promise, 当Promise被通过,它会返回一个Response
对象,通过该对象的json()
方法可以将结果作为JSON对象返回。 response.json()
同样会返回一个Promise
对象,因此在我们的例子中可以继续链接一个then()
方法。
为了能够和传统的XMLHttpRequest
进行对比,我们使用传统的方法来编写一个同样功能的函数:
1
2
3
4
5
6
7
8
function xhrDemo ( ) {
var xhr = new XMLHttpRequest ( ) ;
xhr . onload = function ( ) {
insertPhotos ( JSON . parse ( xhr . responseText ) ) ;
} ;
xhr . open ( 'GET' , URL ) ;
xhr . send ( ) ;
}
可以发现,主要的不同点在于:传统上我们会使用事件处理器,而不是Promise对象。 并且请求的发起完全依赖于xhr
对象所提供的方法。
到目前为止,相比传统的XMLHttpRequest
对象,我们使用Fetch API获得了更简洁的编码体验。但Fetch API不止于此, 下面我们进一步的深入下去。
为什么需要替代XMLHttpRequest
看了前面的例子,你可能会问,为什么不直接使用那些现有的XMLHttpRequest
包装器呢? 原因在于Fetch API不仅仅为你提供了一个fetch()
方法。
对于传统的XMLHttpRequest
而言,你必须使用它的一个实例来执行请求和检索返回的响应。 但是通过Fetch API,我们还能够明确的配置请求对象。
你可以通过Request
构造器函数创建一个新的请求对象,这也是建议标准的一部分。 第一个参数是请求的URL,第二个参数是一个选项对象,用于配置请求。请求对象一旦创建了, 你便可以将所创建的对象传递给fetch()
方法,用于替代默认的URL字符串。示例代码如下:
1
2
3
4
5
6
var req = new Request ( URL , { method : 'GET' , cache : 'reload' } ) ;
fetch ( req ) . then ( function ( response ) {
return response . json ( ) ;
} ) . then ( function ( json ) {
insertPhotos ( json ) ;
} ) ;
上面的代码中我们指明了请求使用的方法为GET
,并且指定不缓存响应的结果。
有关Request
对象的另一件更酷的事在于,你还可以基于原有的对象创建一个新的对象。 新的请求和旧的并没有什么不同,但你可以通过稍微调整配置对象,将其用于不同的场景。 例如,你可以基于原有的GET请求创建一个POST请求,它们具有相同的请求源。代码如下:
1
2
// 基于req对象创建新的postReq对象
var postReq = new Request ( req , { method : 'POST' } ) ;
每个Request
对象都有一个header
属性,在Fetch API中它对应了一个Headers
对象。 通过Headers
对象,你能够修改请求头。不仅如此,对于返回的响应,你还能轻松的返回响应头中的各个属性。 但是需要注意的是,响应头是只读的。
1
2
3
4
5
6
7
var headers = new Headers ( ) ;
headers . append ( 'Accept' , 'application/json' ) ;
var request = new Request ( URL , { headers : headers } ) ;
fetch ( request ) . then ( function ( response ) {
console . log ( response . headers ) ;
} ) ;
在上面的代码中,你可以通过Headers
构造器来获取这个对象,用于为新的Request
对象配置请求头。
相似的,你可以创建一个Response
对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function responseDemo ( ) {
var headers = new Headers ( {
'Content-Type' : 'application/json' ,
'Cache-Control' : 'max-age=3600'
} ) ;
var response = new Response (
JSON . stringify ( { photos : { photo : [ ] } } ) ,
{ status : 200 , headers : headers }
) ;
response . json ( ) . then ( function ( json ) {
insertPhotos ( json ) ;
} ) ;
}
Request
和Response
都完全遵循HTTP标准。如果你曾经使用过某种服务器端语言,你应该对它们很熟悉。 但是对于浏览器而言创建HTTP响应的要点是什么?总之,你不能将它发送给其他人。但是, 你可以通过Service Worker API将响应发送给你自己。 Service Worker允许通过截取来自浏览器的请求头和提供本地构造的响应头来替换来自服务器的响应头的方式来构建离线应用。 你需要注意的是,在本文写作的时候Service Worker仍然是实验性的,并且仍处在不断变化之中。
Fetch API面临的阻力
Fetch API从提出到实现一直存在着争议,由于一直现存的历史原因(例如HTML5的拖拽API被认为太过稀疏平常,Web Components标准被指意义不大)。 因此重新设计一个新的API来替代久经沙场历练的XMLHttpRequest
就变得阻力重重。
其中一种反对观点认为,Promises缺少了一些重要的XMLHttpRequest
的使用场景。例如, 使用标准的ES6 Promise你无法收集进入信息或中断请求。而Fetch的狂热开发者更是试图提供Promise API的扩展用于取消一个Promise。 这个提议有点自挖墙角的意思,因为将这将让Promise变得不符合标准。但这个提议或许会导致未来出现一个可取消的Promise标准。 但另一方面,使用XMLHttpRequest
你可以模拟进度(监听progress
事件),也可以取消请求(使用abort()
方法)。 但是,如果有必要你也可以使用Promise来包裹它。
另一种反对观点认为,Web平台需要的是更多底层的API,而不是高层的API。对此的回答恰恰是, Fetch API足够底层,因为当前的WHATWG标准定义了XMLHttpRequest.send()
方法其实等同于fetch的Requset
对象。 Fetch中的Response.body
实现了getReader()
方法用于渐增的读取原始字节流。 例如,如果照片列表过大而放不进内存的话,你可以使用下面的方法来处理:
1
2
3
4
5
6
7
8
9
10
11
function streamingDemo ( ) {
var req = new Request ( URL , { method : 'GET' , cache : 'reload' } ) ;
fetch ( req ) . then ( function ( response ) {
var reader = response . body . getReader ( ) ;
return reader . read ( ) ;
} ) . then ( function ( result , done ) {
if ( ! done ) {
// do something with each chunk
}
} ) ;
}
在上面的代码中处理器函数一块一块的接收响应体,而不是一次性的。当数据全部被读完后会将done
标记设置为true。 在这种方式下,每次你只需要处理一个chunk,而不是一次性的处理整个响应体。
不幸的是,对于Stream API而言,这仍然还处于早期阶段,这种方式下,如果你需要解析JSON, 你仍然需要从头实现很多的工作。
浏览器支持
Fetch API
目前Chrome 42+, Opera 29+, 和Firefox 39+都支持Fetch。微软也考虑在未来的版本中支持Fetch。 讽刺的是,当IE浏览器终于微响应实现了progress事件的时候,XMLHttpRequest
也走到了尽头。 目前,如果你需要支持IE的话,你需要使用一个polyfill库。
总结
在本文中我们为你介绍了Fetch API的整体概况以及它所能解决的问题。在表层, 这个API看起来非常的简单,但它同时也与一些底层的API相关联,例如Streams, 这让客户端编程有点类似于系统编程。
此外,本文中的代码示例你可以参考这个仓库。
References
- 传统Ajax已死,Fetch永生
- That’s so Fetch!
- Ain’t that fetch!
- Fetch API on MDN