Fetch概念
fetch身為H5中的一個新對象膀篮,他的誕生,是為了取代ajax的存在而出現(xiàn)晃洒,主要目的僅僅只是為了結(jié)合ServiceWorkers垒玲,來達到以下優(yōu)化:
- 優(yōu)化離線體驗
- 保持可擴展性
當然如果ServiceWorkers和瀏覽器端的數(shù)據(jù)庫IndexedDB配合,那么恭喜你岖免,每一個瀏覽器都可以成為一個代理服務(wù)器一樣的存在岳颇。(然而我并不認為這樣是好事,這樣會使得前端越來越重颅湘,走以前c/s架構(gòu)的老路)
能力檢測
如果你想知道自己的瀏覽器是否支持Fetch话侧,只需要簡單new Request或new Response或 new Headers試試就知道了。對于這三個對象是不是非常眼熟闯参,對沒錯瞻鹏,這就是http三劍客,請求鹿寨,響應(yīng)和頭對象新博。
當然那些檢測還是很麻煩,最簡單實用的就是這樣做
if (window.fetch) {
//用fetch做一些事情
}else{
//用ajax做一些事情
}
注意:文章通篇在對比的是fetch和原生ajax(既XMLHttpRequest)脚草,而不是jq中被包裝過的ajax赫悄。
簡單的fetch請求
現(xiàn)在我們來設(shè)置一個非常簡單且基本的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;
});
當然你要是看不懂這個寫法馏慨,也沒有關(guān)系埂淮,可以先去學(xué)習(xí)下ES6中promise的用法,或者promise/A+規(guī)范写隶;
在這邊我們請求了一個flower.jpg的圖片倔撞,請求完成之后用URL.createObjectURL的方式轉(zhuǎn)化為一個url,最后把它賦予img節(jié)點的src屬性樟澜。
當然這邊的response對象的blob方法返回的是一個promise對象误窖,所以可以這樣鏈式調(diào)用叮盘。
注意:如果不做特別設(shè)置,默認情況下是get方法霹俺,如果想要使用別的方法柔吼,或者需要設(shè)置特別的http頭什么的,則需要用到headers和request對象丙唧,或者fetch的額外選項(感覺只是對headers和request的包裝)愈魏。
設(shè)置request的fetch請求
你可以通過request的構(gòu)造函數(shù)新建一個request對象,并把它做為參數(shù)傳入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的構(gòu)造函數(shù)培漏,這樣的我們就可以獲得一個拷貝之后的request對象。
var anotherRequest = new Request(myRequest,myInit);
Headers對象
Headers對象允許你通過Header構(gòu)造函數(shù)來創(chuàng)建胡本,一個Headers對象只是一個簡單的鍵值對集合的map牌柄,當然,里面的鍵值對必須符合http協(xié)議侧甫。(由此可見《http權(quán)威指南》是一本好書)珊佣。
- 設(shè)置Headers對象內(nèi)容的方式一:
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");
- 設(shè)置Headers對象內(nèi)容的方式二:
myHeaders = new Headers({
"Content-Type": "text/plain",
"Content-Length": content.length.toString(),
"X-Custom-Header": "ProcessThisImmediately"
});
當然Headers里面的內(nèi)容是可以查詢和設(shè)置:
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設(shè)置了一個不符合http協(xié)議規(guī)范中的key,(比如你對request的header設(shè)置了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!");
}
一個比較好的用例,就是在處理數(shù)據(jù)之前守屉,先檢查下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的屬性,該屬性控制著該頭節(jié)點是否可以設(shè)置別的key-value值(鍵值對)拇泛。目前在web中Guard的屬性并沒有被暴露出來滨巴,因此你在瀏覽器中是無法獲得該屬性的。
Guard擁有以下幾個屬性
none: 默認的.
request: 對request對象守護 (Request.headers).
request-no-cors: 設(shè)置Request.mode 的值為no-cors碰镜,request中的headers守護模式值兢卵。
response:Response.headers的守護級別。
immutable: 經(jīng)常用于ServiceWorkers; headers 變?yōu)橹蛔x绪颖。
** 注意:你也許無法在requeset中設(shè)置"Content-Length",類似的甜奄,在response中無法設(shè)置"Set-Cookie"柠横,因為在ServiceWorkers中是不被允許設(shè)置cookie的。
Response
正如你在上面看到的课兄,response只有在fetch對象的狀態(tài)處于resolved的情況下才會在返回牍氛。(其實是只有當fetch這個異步操作成功之后,response對象會作為一個參數(shù)烟阐,傳入到回掉函數(shù)中搬俊。)
當然我們也可以通過Response的構(gòu)造函數(shù)來實現(xiàn)紊扬,但是這個對象只有在ServiceWorkers中是有用的,當然你也可以在window中監(jiān)聽fetch事件唉擂,然后調(diào)用event的responseWith方法中使用餐屎,如下:
var myBody = new Blob();
addEventListener('fetch', function(event) {
event.respondWith( new Response(myBody, {
headers: {
"Content-Type" : "text/plain" }
})
);
} );
以下是我們經(jīng)常用的到的response屬性:
- Response.status :響應(yīng)的狀態(tài)碼。
- Response.statusText :響應(yīng)狀態(tài)文玩祟。
- Response.ok :返回的是一個Boolean值腹缩,只要狀態(tài)碼在200-299之間,就返回true空扎。
** 注意:Response的靜態(tài)方法中 error() 和redirect()返回的是也都是Response對象藏鹊,但是只有在Service Workers中有用。
Body
這邊的body指的是請求和響應(yīng)的體转锈,支持一下幾種數(shù)據(jù)類型:
- ArrayBuffer
- ArrayBufferView (Uint8Array and friends)
- Blob/File
- string
- URLSearchParams
- FormData
當然body都有相對的擴展方法去獲取這些類型的數(shù)據(jù)盘寡,這些方法返回的值是一個promise對象,基本上都是基于stream(流)的思想撮慨。
于是我們就可以用以下的方式來使用fetch:
var form = new FormData(document.getElementById('login-form'));
fetch("/login", {
method: "POST",
body: form
})
不要擔(dān)心在傳這些數(shù)據(jù)的時候宴抚,Content-type的值,瀏覽器會智能的幫我們設(shè)置這些甫煞,不管在request中還是response中菇曲,當然你要是手工指定也是可以的。
總結(jié)
fetch相比較原生ajax更為靈活抚吠,提供的數(shù)據(jù)類型更多常潮,更接近底層。但是目前看來楷力,想要取代ajax喊式,還需要一段時間,因為ajax二代+類jQuery工具庫的幫助萧朝,fetch在常規(guī)應(yīng)用方面還是相形見絀的岔留。