Fetch概念
fetch身為H5中的一個(gè)新對(duì)象,他的誕生,是為了取代ajax的存在而出現(xiàn),主要目的僅僅只是為了結(jié)合ServiceWorkers,來達(dá)到以下優(yōu)化:
- 優(yōu)化離線體驗(yàn)
- 保持可擴(kuò)展性
當(dāng)然如果ServiceWorkers和瀏覽器端的數(shù)據(jù)庫IndexedDB配合,那么恭喜你,每一個(gè)瀏覽器都可以成為一個(gè)代理服務(wù)器一樣的存在。(然而我并不認(rèn)為這樣是好事,這樣會(huì)使得前端越來越重,走以前c/s架構(gòu)的老路)
能力檢測(cè)
如果你想知道自己的瀏覽器是否支持Fetch,只需要簡單new Request或new Response或 new Headers試試就知道了。對(duì)于這三個(gè)對(duì)象是不是非常眼熟,對(duì)沒錯(cuò),這就是http三劍客,請(qǐng)求,響應(yīng)和頭對(duì)象。
當(dāng)然那些檢測(cè)還是很麻煩,最簡單實(shí)用的就是這樣做
if (window.fetch) {
//用fetch做一些事情
}else{
//用ajax做一些事情
}
注意:文章通篇在對(duì)比的是fetch和原生ajax(既XMLHttpRequest),而不是jq中被包裝過的ajax。
簡單的fetch請(qǐng)求
現(xiàn)在我們來設(shè)置一個(gè)非常簡單且基本的fetch請(qǐng)求,如下代碼:
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;
});
當(dāng)然你要是看不懂這個(gè)寫法,也沒有關(guān)系,可以先去學(xué)習(xí)下ES6中promise的用法,或者promise/A+規(guī)范;
在這邊我們請(qǐng)求了一個(gè)flower.jpg的圖片,請(qǐng)求完成之后用URL.createObjectURL的方式轉(zhuǎn)化為一個(gè)url,最后把它賦予img節(jié)點(diǎn)的src屬性。
當(dāng)然這邊的response對(duì)象的blob方法返回的是一個(gè)promise對(duì)象,所以可以這樣鏈?zhǔn)秸{(diào)用。
注意:如果不做特別設(shè)置,默認(rèn)情況下是get方法,如果想要使用別的方法,或者需要設(shè)置特別的http頭什么的,則需要用到headers和request對(duì)象,或者fetch的額外選項(xiàng)(感覺只是對(duì)headers和request的包裝)。
設(shè)置request的fetch請(qǐng)求
你可以通過request的構(gòu)造函數(shù)新建一個(gè)request對(duì)象,并把它做為參數(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;
});
當(dāng)然我們也可以通過把一個(gè)舊的request對(duì)象傳入一個(gè)request的構(gòu)造函數(shù),這樣的我們就可以獲得一個(gè)拷貝之后的request對(duì)象。
var anotherRequest = new Request(myRequest,myInit);
Headers對(duì)象
Headers對(duì)象允許你通過Header構(gòu)造函數(shù)來創(chuàng)建,一個(gè)Headers對(duì)象只是一個(gè)簡單的鍵值對(duì)集合的map,當(dāng)然,里面的鍵值對(duì)必須符合http協(xié)議。(由此可見《http權(quán)威指南》是一本好書)。
- 設(shè)置Headers對(duì)象內(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對(duì)象內(nèi)容的方式二:
myHeaders = new Headers({
"Content-Type": "text/plain",
"Content-Length": content.length.toString(),
"X-Custom-Header": "ProcessThisImmediately"
});
當(dāng)然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
中有用。
如果你對(duì)header的key設(shè)置了一個(gè)不符合http協(xié)議規(guī)范中的key,(比如你對(duì)request的header設(shè)置了response特有的header),那么js在嚴(yán)格守護(hù)的模式下會(huì)報(bào)TypeError的錯(cuò)誤。例如:
var myResponse = Response.error();
try {
myResponse.headers.set("Origin", "http://mybank.com");
} catch(e) {
console.log("Cannot pretend to be a bank!");
}
一個(gè)比較好的用例,就是在處理數(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(守護(hù)模式)
每一個(gè)Headers對(duì)象都有一個(gè)Guard的屬性,該屬性控制著該頭節(jié)點(diǎn)是否可以設(shè)置別的key-value值(鍵值對(duì))。目前在web中Guard的屬性并沒有被暴露出來,因此你在瀏覽器中是無法獲得該屬性的。
Guard擁有以下幾個(gè)屬性
none: 默認(rèn)的.
request: 對(duì)request對(duì)象守護(hù) (Request.headers).
request-no-cors: 設(shè)置Request.mode 的值為no-cors,request中的headers守護(hù)模式值。
response:Response.headers的守護(hù)級(jí)別。
immutable: 經(jīng)常用于ServiceWorkers; headers 變?yōu)橹蛔x。
** 注意:你也許無法在requeset中設(shè)置"Content-Length",類似的,在response中無法設(shè)置"Set-Cookie",因?yàn)樵赟erviceWorkers中是不被允許設(shè)置cookie的。
Response
正如你在上面看到的,response只有在fetch對(duì)象的狀態(tài)處于resolved的情況下才會(huì)在返回。(其實(shí)是只有當(dāng)fetch這個(gè)異步操作成功之后,response對(duì)象會(huì)作為一個(gè)參數(shù),傳入到回掉函數(shù)中。)
當(dāng)然我們也可以通過Response的構(gòu)造函數(shù)來實(shí)現(xiàn),但是這個(gè)對(duì)象只有在ServiceWorkers中是有用的,當(dāng)然你也可以在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 :返回的是一個(gè)Boolean值,只要狀態(tài)碼在200-299之間,就返回true。
** 注意:Response的靜態(tài)方法中 error() 和redirect()返回的是也都是Response對(duì)象,但是只有在Service Workers中有用。
Body
這邊的body指的是請(qǐng)求和響應(yīng)的體,支持一下幾種數(shù)據(jù)類型:
- ArrayBuffer
- ArrayBufferView (Uint8Array and friends)
- Blob/File
- string
- URLSearchParams
- FormData
當(dāng)然body都有相對(duì)的擴(kuò)展方法去獲取這些類型的數(shù)據(jù),這些方法返回的值是一個(gè)promise對(duì)象,基本上都是基于stream(流)的思想。
于是我們就可以用以下的方式來使用fetch:
var form = new FormData(document.getElementById('login-form'));
fetch("/login", {
method: "POST",
body: form
})
不要擔(dān)心在傳這些數(shù)據(jù)的時(shí)候,Content-type的值,瀏覽器會(huì)智能的幫我們?cè)O(shè)置這些,不管在request中還是response中,當(dāng)然你要是手工指定也是可以的。
總結(jié)
fetch相比較原生ajax更為靈活,提供的數(shù)據(jù)類型更多,更接近底層。但是目前看來,想要取代ajax,還需要一段時(shí)間,因?yàn)閍jax二代+類jQuery工具庫的幫助,fetch在常規(guī)應(yīng)用方面還是相形見絀的。