关于ajax,一个人尽皆知的秘密是,它的基础API:XMLHttpRequest,用它来做ajax请求实在是不太合适。我们已经在创造一个优雅的,关于xhr的一些API上做得很好了,但是我们能做得更好,而能让我们做得更好的就是使用fetch API!下面我们一起简单地看看这个新增的window.fetch方法,目前已经可以在Firefox和Chrome Canary上使用了。(注:现在已经可以在最新的Chrome稳定版本上使用)
XMLHttpRequest
在我看来XHR有点儿复杂了,而且我有点想吐槽的是,为什么XML是大写的,Http却是小写的???无论如何,这就是现在你用XHR时的写法:
//XHR,简直了。。。
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','https://davidwalsh.name/ajax-endpoint',true);
request.send(null);
复制代码
当然我们的js框架让我们使用XHR的方式变得更优雅了,但是从上面的例子你可以看出XHR真是一团糟。
基本的Fetch用法
在全局window作用域现在就提供了fetch方法,第一个参数是一个URL:
//url参数是必须的,而options是可选的
fetch('https://davidwalsh.name/some/url', {
method: 'get'
}).then(function(response) {
}).catch(function(err) {
// Error :(
});
复制代码
和最新的Battery API很像,fetch API 是使用Promise去处理结果或者回调的:
// 简单的响应处理
fetch('https://davidwalsh.name/some/url').then(function(response) {
}).catch(function(err) {
// Error :(
});
// 挂上更“高级”的处理逻辑
fetch('https://davidwalsh.name/some/url').then(function(response) {
return //...
}).then(function(returnedValue) {
// ...
}).catch(function(err) {
// Error :(
});
复制代码
如果你不习惯使用then,最好还是习惯一下吧。。。它很快就会无处不在
请求头
能够设置请求头对请求的灵活性来说很重要。你可以使用new Headers()来创建一个请求头
// 创建一个空的请求头实例
var headers = new Headers();
// 添加一些头部字段
headers.append('Content-Type', 'text/plain');
headers.append('X-My-Custom-Header', 'CustomValue');
// 检查、获取和设置头部的字段
headers.has('Content-Type'); // true
headers.get('Content-Type'); // "text/plain"
headers.set('Content-Type', 'application/json');
// 删除一个头部字段
headers.delete('X-My-Custom-Header');
// 添加初始化信息
var headers = new Headers({
'Content-Type': 'text/plain',
'X-My-Custom-Header': 'CustomValue'
});
复制代码
你可以用append
,has
,get
,set
和delete
方法去更改请求头。可以通过创建一个请求实例去使用这个请求头。
var request = new Request('https://davidwalsh.name/some-url', {
headers: new Headers({
'Content-Type': 'text/plain'
})
});
fetch(request).then(function() { /* 处理响应 */ });
复制代码
我们来看看Request和Response做了些什么吧!
Request
一个 Request
实例代表着一个 fetch
方法调用的片段。通过交给 fetch
一个 Request
对象你可以创建一个高级的、定制的请求:
- method - GET, POST, PUT, DELETE, HEAD
- url - 请求的url
- headers - 和
Headers
对象有关 - referrer - 请求的referrer
- mode - cors, no-cors, same-origin几个模式
- credentials - 设置cookie是否随请求一起发送,当然了,有同源策略
- redirect - follow, error, manual
- integrity - SRI的值
- cache - 缓存模式 (default, reload, no-cache)
一个简单的Request 用起来起来就像:
var request = new Request('https://davidwalsh.name/users.json', {
method: 'POST',
mode: 'cors',
redirect: 'follow',
headers: new Headers({
'Content-Type': 'text/plain'
})
});
// 现在,使用它!
fetch(request).then(function() { /* 处理响应 */ });
复制代码
只有第一个参数url是必须的。所有的属性会在Request实例创建后变成只读的状态。需要留意的是 Request
有一个 clone
方法,当在Service Worker API 中使用 fetch
时这个方法非常重要。因为一个 Request
是一个stream
,当另外一个fetch
调用时必须被克隆。
fetch
方法的用法,表现得就像 Request
一样,所以你可以这样做:
fetch('https://davidwalsh.name/users.json', {
method: 'POST',
mode: 'cors',
redirect: 'follow',
headers: new Headers({
'Content-Type': 'text/plain'
})
}).then(function() { /* 处理响应 */ });
复制代码
在Service Worker中你可能只会用到Request
实例,因为Request
和fetch
的用法可以说是一样的。
Response
fetch
的then
方法提供了一个Response
实例,但你可以手动创建一个Response对象——一种你可能会在使用service workers时遇到的情况。你可以这样设置Response
:
- type - basic, cors
- url
- useFinalURL - Boolean值,说明
url
是不是最后的URL - status - 状态码 (比如: 200, 404。。。)
- ok - Boolean 值,是否成功响应 (200到299间的状态)
- statusText - 状态码的文字描述 (比如: OK)
- headers - 和response 关联的Headers 对象
// 创建你自己的响应,以测试Service worker
// new Response(BODY, OPTIONS)
var response = new Response('.....', {
ok: false,
status: 404,
url: '/'
});
// fetch的then方法会拿到一个Response 实例
fetch('https://davidwalsh.name/')
.then(function(responseObj) {
console.log('status: ', responseObj.status);
});
复制代码
Response 对象也提供了这些方法:
- clone() - 创建一个Response对象的拷贝
- error() - 返回一个新的关于网络错误的Response对象
- redirect() - 创建一个新的不同url的响应
- arrayBuffer() - 返回一个promise,一个携带ArrayBuffer的
resolve
- blob() - 返回一个promise,一个携带Blob的
resolve
- formData() - 返回一个返回一个promise,一个携带formData对象的
resolve
- json() - 返回一个返回一个promise对象,一个携带json的
resolve
- text() - 返回一个返回一个promise对象,一个携带
USVString
(text)的resolve
处理JSON
当你创建了一个请求,而请求的是JSON,那么它所得到的回调函数将具有把原始数据转换为JavaScript对象的json
方法
fetch('https://davidwalsh.name/demo/arsenal.json').then(function(response) {
// Convert to JSON
return response.json();
}).then(function(j) {
// 没错,j是个JavaScript 对象
console.log(j);
});
复制代码
当然了,这就是一个简单的JSON.parse(jsonString),不过使用json
方法是一条方便的捷径。
处理基本的文本/HTML响应
JSON 不会一直是我们期望的响应格式,所以这边你可以看看怎么处理文本或者HTML响应。
fetch('/next/page')
.then(function(response) {
return response.text();
}).then(function(text) {
// <!DOCTYPE ....
console.log(text);
});
复制代码
你可以通过在text()
方法后链接上Promise的 then
方法来获得响应文本。
处理Blob响应
如果你想通过 fetch
来加载一张图片,就会有点区别,举个栗子:
fetch('https://davidwalsh.name/flowers.jpg')
.then(function(response) {
return response.blob();
})
.then(function(imageBlob) {
document.querySelector('img').src = URL.createObjectURL(imageBlob);
});
复制代码
响应的Blob()
方法接受一个Response流并将其读取完成。
发送From Data
另外一个通常会使用AJAX的情况就是发送From Data,这里我们来看看怎么用 fetch
方法来发送From Data:
fetch('https://davidwalsh.name/submit-json', {
method: 'post',
body: JSON.stringify({
email: document.getElementById('email').value,
answer: document.getElementById('answer').value
})
});
复制代码
非常的简单,非常的赏心悦目!
不成文的小故事
或许 fetch
是一个更好的API,但这个API目前不支持取消请求,这会让很多开发者对它失望。
看起来使用这个新的 fetch
API比起使用XHR来说更明智,更简单,托他的福我们可以更正确的去实现AJAX。fetch
有着“事后诸葛亮”的优势,尽管 fetch
还没被广泛支持,但我已经迫不及待了!
这是一篇关于 fetch
的介绍。想了解更多的话请移步到Introduction to Fetch。如果你想找 polyfill
的方案,可以看看GitHub's implementation。