Fetch:ajax替代品(譯文)

Fetch概念

fetch身為H5中的一個(gè)新對(duì)象,他的誕生,是為了取代ajax的存在而出現(xiàn),主要目的僅僅只是為了結(jié)合ServiceWorkers,來達(dá)到以下優(yōu)化:

  1. 優(yōu)化離線體驗(yàn)
  2. 保持可擴(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ù)類型:

當(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)用方面還是相形見絀的。

參考資料:
Using Fetch
[譯] JavaScript Fetch API

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容