Fetch概念
fetch身为H5中的一个新对象,他的诞生,是为了取代ajax的存在而出现,主要目的仅仅只是为了结合ServiceWorkers,来达到以下优化:
- 优化离线体验
- 保持可扩展性
当然如果ServiceWorkers和浏览器端的数据库IndexedDB配合,那么恭喜你,每一个浏览器都可以成为一个代理服务器一样的存在。(然而我并不认为这样是好事,这样会使得前端越来越重,走以前c/s架构的老路)
能力检测
如果你想知道自己的浏览器是否支持Fetch,只需要简单new Request或new Response或 new Headers试试就知道了。对于这三个对象是不是非常眼熟,对没错,这就是http三剑客,请求,响应和头对象。
当然那些检测还是很麻烦,最简单实用的就是这样做
if (window.fetch) {
//用fetch做一些事情
}else{
//用ajax做一些事情
}
注意:文章通篇在对比的是fetch和原生ajax(既XMLHttpRequest),而不是jq中被包装过的ajax。
简单的fetch请求
现在我们来设置一个非常简单且基本的fetch请求,如下代码:
var img =document.querySelector("img");
fetch('flower.jpg').then(function(response){
return response.blob();
}).then(function(myblob){
var objectURL = URL.createObjectURL(myBlob);
myImage.src =objectURL;
});
当然你要是看不懂这个写法,也没有关系,可以先去学习下ES6中promise的用法,或者promise/A+规范;
在这边我们请求了一个flower.jpg的图片,请求完成之后用URL.createObjectURL的方式转化为一个url,最后把它赋予img节点的src属性。
当然这边的response对象的blob方法返回的是一个promise对象,所以可以这样链式调用。
注意:如果不做特别设置,默认情况下是get方法,如果想要使用别的方法,或者需要设置特别的http头什么的,则需要用到headers和request对象,或者fetch的额外选项(感觉只是对headers和request的包装)。
设置request的fetch请求
你可以通过request的构造函数新建一个request对象,并把它做为参数传入fetch中,类似下面这样:
var myHeaders = new Headers();
var myInit = {
method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default'
};
var myRequest = new Request('flowers.jpg',myInit);
fetch(myRequest, myInit)
.then(function(response) {
return response.blob();
})
.then(function(myBlob) {
var objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
});
当然我们也可以通过把一个旧的request对象传入一个request的构造函数,这样的我们就可以获得一个拷贝之后的request对象。
var anotherRequest = new Request(myRequest,myInit);
Headers对象
Headers对象允许你通过Header构造函数来创建,一个Headers对象只是一个简单的键值对集合的map,当然,里面的键值对必须符合http协议。(由此可见《http权威指南》是一本好书)。
- 设置Headers对象内容的方式一:
var content = "Hello World";
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Content-Length", content.length.toString());
myHeaders.append("X-Custom-Header", "ProcessThisImmediately");
- 设置Headers对象内容的方式二:
myHeaders = new Headers({
"Content-Type": "text/plain",
"Content-Length": content.length.toString(),
"X-Custom-Header": "ProcessThisImmediately"
});
当然Headers里面的内容是可以查询和设置:
console.log(myHeaders.has("Content-Type")); // true
console.log(myHeaders.has("Set-Cookie")); // false
myHeaders.set("Content-Type", "text/html");
myHeaders.append("X-Custom-Header", "AnotherValue");
console.log(myHeaders.get("Content-Length")); // 11
console.log(myHeaders.getAll("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]
myHeaders.delete("X-Custom-Header");
console.log(myHeaders.getAll("X-Custom-Header")); // [ ]
但是这里面的有些操作仅仅在 ServiceWorkers
中有用。
如果你对header的key设置了一个不符合http协议规范中的key,(比如你对request的header设置了response特有的header),那么js在严格守护的模式下会报TypeError的错误。例如:
var myResponse = Response.error();
try {
myResponse.headers.set("Origin", "http://mybank.com");
} catch(e) {
console.log("Cannot pretend to be a bank!");
}
一个比较好的用例,就是在处理数据之前,先检查下response中的content-type值。例如:
fetch(myRequest)
.then(function(response) {
var contentType = response.headers.get("content-type");
if(contentType && contentType.indexOf("application/json") !== -1) {
return response.json().then(function(json) {
// process your JSON further
});
} else {
console.log("Oops, we haven't got JSON!");
}
});
Guard(守护模式)
每一个Headers对象都有一个Guard的属性,该属性控制着该头节点是否可以设置别的key-value值(键值对)。目前在web中Guard的属性并没有被暴露出来,因此你在浏览器中是无法获得该属性的。
Guard拥有以下几个属性
none: 默认的.
request: 对request对象守护 (Request.headers).
request-no-cors: 设置Request.mode 的值为no-cors,request中的headers守护模式值。
response:Response.headers的守护级别。
immutable: 经常用于ServiceWorkers; headers 变为只读。
** 注意:你也许无法在requeset中设置"Content-Length",类似的,在response中无法设置"Set-Cookie",因为在ServiceWorkers中是不被允许设置cookie的。
Response
正如你在上面看到的,response只有在fetch对象的状态处于resolved的情况下才会在返回。(其实是只有当fetch这个异步操作成功之后,response对象会作为一个参数,传入到回掉函数中。)
当然我们也可以通过Response的构造函数来实现,但是这个对象只有在ServiceWorkers中是有用的,当然你也可以在window中监听fetch事件,然后调用event的responseWith方法中使用,如下:
var myBody = new Blob();
addEventListener('fetch', function(event) {
event.respondWith( new Response(myBody, {
headers: {
"Content-Type" : "text/plain" }
})
);
} );
以下是我们经常用的到的response属性:
- Response.status :响应的状态码。
- Response.statusText :响应状态文。
- Response.ok :返回的是一个Boolean值,只要状态码在200-299之间,就返回true。
** 注意:Response的静态方法中 error() 和redirect()返回的是也都是Response对象,但是只有在Service Workers中有用。
Body
这边的body指的是请求和响应的体,支持一下几种数据类型:
- ArrayBuffer
- ArrayBufferView (Uint8Array and friends)
- Blob/File
- string
- URLSearchParams
- FormData
当然body都有相对的扩展方法去获取这些类型的数据,这些方法返回的值是一个promise对象,基本上都是基于stream(流)的思想。
于是我们就可以用以下的方式来使用fetch:
var form = new FormData(document.getElementById('login-form'));
fetch("/login", {
method: "POST",
body: form
})
不要担心在传这些数据的时候,Content-type的值,浏览器会智能的帮我们设置这些,不管在request中还是response中,当然你要是手工指定也是可以的。
总结
fetch相比较原生ajax更为灵活,提供的数据类型更多,更接近底层。但是目前看来,想要取代ajax,还需要一段时间,因为ajax二代+类jQuery工具库的帮助,fetch在常规应用方面还是相形见绌的。