Fetch使用介紹

一、瀏覽器同策同源

可謂同源?URL由協議、域名、端口和路徑組成,若是兩個URL的協議、域名和端口相同,則表示他們同源。瀏覽器的同源策略,限制了來自不一樣源 的"document"或腳本,對當前"document"讀取或設置某些屬性,即從一個域上加載的腳本不容許訪問另一個域的文檔屬性。好比一個惡意網 站的頁面經過iframe嵌入了銀行的登陸頁面(兩者不一樣源),若是沒有同源限制,惡意網頁上的javascript腳本就能夠在用戶登陸銀行的時候獲取 用戶名和密碼。所謂道高一尺魔高一丈,雖然瀏覽器以同源策略限制了咱們隨意請求資源,可是從這個策略出現開始就有不少各類各樣的Hacker技巧來。javascript

二、四種跨域方法的實現

一、Jsonpphp

二、CORS:跨域資源共享html

三、postMessagejava

具體原理和用法請參照:http://www.javashuo.com/article/p-slymzbdg-h.htmljquery

三、Fetch介紹

3.一、Fetch定義

Fetch API提供了一個fetch()方法,它被定義在BOM的window對象中,你能夠用它來發起對遠程資源的請求。 該方法返回的是一個ES6的Promise對象,讓你可以對請求的返回結果進行檢索。 它是 W3C 的正式標準 。git

3.二、Fetch所面臨的阻力

Fetch API從提出到實現一直存在着爭議,因爲一直現存的歷史緣由(例如HTML5的拖拽API被認爲太過稀疏日常,Web Components標準被指意義不大)。 所以從新設計一個新的API來替代久經沙場歷練的XMLHttpRequest就變得阻力重重。es6

其中一種反對觀點認爲,Promises缺乏了一些重要的XMLHttpRequest的使用場景。例如, 使用標準的ES6 Promise你沒法收集進入信息或中斷請求。而Fetch的狂熱開發者更是試圖提供Promise API的擴展用於取消一個Promise。 這個提議有點自挖牆角的意思,由於將這將讓Promise變得不符合標準。但這個提議或許會致使將來出現一個可取消的Promise標準。 但另外一方面,使用XMLHttpRequest你能夠模擬進度(監聽progress事件),也能夠取消請求(使用abort()方法)。 可是,若是有必要你也可使用Promise來包裹它。github

另外一種反對觀點認爲,Web平臺須要的是更多底層的API,而不是高層的API。對此的回答偏偏是, Fetch API足夠底層,由於當前的WHATWG標準定義了XMLHttpRequest.send()方法其實等同於fetch的Requset對象。 Fetch中的Response.body實現了getReader()方法用於漸增的讀取原始字節流。 例如,若是照片列表過大而放不進內存的話,你可使用下面的方法來處理:web

function streamingDemo() {  
    var req = new Request(URL, {method: 'GET', cache: 'reload'});  
    fetch(req).then(function(response) {  
        var reader = response.body.getReader();  
        return reader.read();  
    }).then(function(result, done) {  
        if (!done) {  
        // do something with each chunk  
        }  
    });  
}

 

在上面的代碼中處理器函數一塊一塊的接收響應體,而不是一次性的。當數據所有被讀完後會將done標記設置爲true。 在這種方式下,每次你只須要處理一個chunk,而不是一次性的處理整個響應體。json

3.三、Fetch的優勢

一、語法簡潔,更加語義化

二、基於標準 Promise 實現,支持 async/await

三、同構方便,使用 isomorphic-fetch

3.四、Fetch的不足

因爲 Fetch 是典型的異步場景,因此大部分遇到的問題不是 Fetch 的,實際上是 Promise 的。ES6 的 Promise 是基於 Promises/A+ 標準,爲了保持 簡單簡潔 ,只提供極簡的幾個 API。若是你用過一些牛 X 的異步庫,如 jQuery(不要笑) 、Q.js 或者 RSVP.js,可能會感受 Promise 功能太少了。

(1)沒有 Deferred

Deferred 能夠在建立 Promise 時能夠減小一層嵌套,還有就是跨方法使用時很方便。
ECMAScript 11 年就有過 Deferred 提案,但後來沒被接受。其實用 Promise 不到十行代碼就能實現 Deferred:es6-deferred。如今有了 async/await,generator/yield 後,deferred 就沒有使用價值了。

(2)沒有獲取狀態方法:isRejected,isResolved

標準 Promise 沒有提供獲取當前狀態 rejected 或者 resolved 的方法。只容許外部傳入成功或失敗後的回調。我認爲這實際上是優勢,這是一種聲明式的接口,更簡單。

(3)缺乏其它一些方法:always,progress,finally

always 能夠經過在 then 和 catch 裏重複調用方法實現。finally 也相似。progress 這種進度通知的功能尚未用過,暫不知道如何替代。

(4)不能中斷,沒有 abort、terminate、onTimeout 或 cancel 方法

Fetch 和 Promise 同樣,一旦發起,不能中斷,也不會超時,只能等待被 resolve 或 reject。幸運的是,whatwg 目前正在嘗試解決這個問題 whatwg/fetch#27

四、Fetch的使用

4.一、簡單使用

(1)get請求

fetch("/data.json").then(function(res) {
  // res instanceof Response == true.
  if (res.ok) {
    res.json().then(function(data) {
      console.log(data.entries);
    });
  } else {
    console.log("Looks like the response wasn't perfect, got status", res.status);
  }
}, function(e) {
  console.log("Fetch failed!", e);
});



fetch('http://nero-zou.com/test.json')  
    .then((response) => {
        if (response.ok) {
            return response.json()
        } else {
            console.error('服務器繁忙,請稍後再試;\r\nCode:' + response.status)
        }
    })
    .then((data) => {
        console.log(data)
    })
    .catch((err)=> {
        console.error(err)
    })

(2)post請求

fetch('/users', {
  method: 'post',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Hubot',
    login: 'hubot',
  })
})



fetch("http://www.example.org/submit.php", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: "firstName=Nikhil&favColor=blue&password=easytoguess"
}).then(function(res) {
  if (res.ok) {
    alert("Perfect! Your settings are saved.");
  } else if (res.status == 401) {
    alert("Oops! You are not authorized.");
  }
}, function(e) {
  alert("Error submitting form!");
});

(3)上傳文件

var input = document.querySelector('input[type="file"]')

var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')

fetch('/avatars', {
  method: 'post',
  body: data
})

4.二、Fetch中的三個主要部分

Fetch 引入了 3 個接口,分別是 HeadersRequestResponse。他們直接對應於的 HTTP 中相應的概念,可是基於隱私和安全考慮,也有些區別,例如支持 CORS 規則以及保證 cookies 不能被第三方獲取。

4.2.一、Header

自定義請求頭信息極大地加強了請求的靈活性。咱們能夠經過 new Headers() 來建立請求頭:

Create an empty Headers instance

var headers = new Headers();



// Add a few headers

headers.append('Content-Type', 'text/plain');

headers.append('X-My-Custom-Header', 'CustomValue');



// Check, get, and set header values

headers.has('Content-Type'); // true

headers.get('Content-Type'); // "text/plain"

headers.set('Content-Type', 'application/json');



// Delete a header

headers.delete('X-My-Custom-Header');



// Add initial values

var headers = new Headers({

'Content-Type': 'text/plain',

'X-My-Custom-Header': 'CustomValue'

});

 

 

4.2.二、Request

一、Request對象表明了一次fetch請求中的請求體部分,你能夠自定義Request對象:

  • method - 使用的HTTP動詞,GET, POST, PUT, DELETE, HEAD

  • url - 請求地址,URL of the request

  • headers - 關聯的Header對象

  • referrer - referrer

  • mode - 請求的模式,主要用於跨域設置,cors, no-cors, same-origin

  • credentials - 是否發送Cookie omit, same-origin

  • redirect - 收到重定向請求以後的操做,follow, error, manual

  • integrity - 完整性校驗

  • cache - 緩存模式(default, reload, no-cache)

 

二、使用方式以下

var request = new Request('/users.json', {

    method: 'POST', 

    mode: 'cors', 

    redirect: 'follow',

    headers: new Headers({

        'Content-Type': 'text/plain'

    })

});



// Now use it!

fetch(request).then(function() { /* handle response */ });

 

三、Request對象中mode屬性詳細介紹

(1)same-origin

該模式很簡單,若是一個請求是跨域的,那麼將返回一個 error,這樣確保全部的請求遵照同源策略。

var arbitraryUrl = document.getElementById("url-input").value;
fetch(arbitraryUrl, { mode: "same-origin" }).then(function(res) {
  console.log("Response succeeded?", res.ok);
}, function(e) {
  console.log("Please enter a same-origin URL!");
});

(2)no-cors

該模式容許來自 CDN 的腳本、其餘域的圖片和其餘一些跨域資源,可是首先有個前提條件,就是請求的 method 只能是HEADGETPOST。此外,若是 ServiceWorkers 攔截了這些請求,它不能隨意添加或者修改除這些以外 Header 屬性。第三,JS 不能訪問 Response 對象中的任何屬性,這確保了跨域時 ServiceWorkers 的安全和隱私信息泄漏問題。

(3)cors

該模式一般用於跨域請求,用來從第三方提供的 API 獲取數據。該模式遵照 CORS 協議,並只有有限的一些 Header 被暴露給 Response 對象,可是 body 是可讀的。例如,獲取一個 Flickr 最感興趣的照片的清單:

var u = new URLSearchParams();
u.append('method', 'flickr.interestingness.getList');
u.append('api_key', '<insert api key here>');
u.append('format', 'json');
u.append('nojsoncallback', '1');
 
var apiCall = fetch('https://api.flickr.com/services/rest?' + u);
 
apiCall.then(function(response) {
  return response.json().then(function(json) {
    // photo is a list of photos.
    return json.photos.photo;
  });
}).then(function(photos) {
  photos.forEach(function(photo) {
    console.log(photo.title);
  });
});

你將沒法從 Headers 中讀取 Date 屬性,由於 Flickr 在 Access-Control-Expose-Headers 中設置了不容許讀取它。

response.headers.get("Date"); // null

 

另外,credentials 屬性決定了是否能夠跨域訪問 cookie 。該屬性與 XHR 的
withCredentials 標誌相同,可是隻有三個值,分別是 omit(默認)、same-origininclude

Request 對象也提供了客戶端緩存機制(caching hints)。這個屬性還在安全複審階段。Firefox 提供了這個屬性,但目前還不起做用。

4.2.三、Response

fetch 返回的 then 方法有一個 response 參數,它是一個 Response 實例。 

Response 有以下屬性:

  • type - basic, cors
  • url
  • useFinalURL - 是否爲最終地址
  • status - 狀態碼 (ex: 200, 404, etc.)
  • ok - 是否成功響應 (status in the range 200-299)
  • statusText - status code (ex: OK)
  • headers - 響應頭

 

Response 有以下方法:

  • clone() - 複製一份response

  • error() - 返回一個與網絡相關的錯誤

  • redirect() - 返回了一個能夠重定向至某URL的response.

  • arrayBuffer() - 返回一個帶有ArrayBuffer的Promise.

  • blob() - 返回一個帶有Blob的Promise.

  • formData() - 返回一個帶有FormData的Promise.

  • json() - 返回一個帶有JSON格式對象的Promise.

  • text() - 返回一個帶有文本的Promise.

 

五、Fetch的瀏覽器兼容

image

原生支持率並不高,幸運的是,引入下面這些 polyfill 後能夠完美支持 IE8+ :

  1. 因爲 IE8 是 ES3,須要引入 ES5 的 polyfill: es5-shim, es5-sham
  2. 引入 Promise 的 polyfill: es6-promise
  3. 引入 fetch 探測庫:fetch-detector
  4. 引入 fetch 的 polyfill: fetch-ie8
  5. 可選:若是你還使用了 jsonp,引入 fetch-jsonp
  6. 可選:開啓 Babel 的 runtime 模式,如今就使用 async/await

能夠經過檢查 HeadersRequestResponsefetch 在 window 或 worker 做用域中是否存在,來檢查是否支持 Fetch API。

Fetch polyfill 的基本原理是探測是否存在 window.fetch 方法,若是沒有則用 XHR 實現。這也是 github/fetch 的作法,可是有些瀏覽器(Chrome 45)原生支持 Fetch,但響應中有中文時會亂碼,老外又不太關心這種問題,因此封裝了 fetch-detectorfetch-ie8 只在瀏覽器穩定支持 Fetch 狀況下才使用原生 Fetch。這些庫如今 天天有幾千萬個請求都在使用,絕對靠譜 !

 

六、Fetch的服務端使用

用到了isomorphic-fetch這個庫

// 自動進行全局的ES6 Promise的Polyfill
require('es6-promise').polyfill(); 
require('isomorphic-fetch');

七、參考文章

http://www.csdn.net/article/1970-01-01/2826065

https://github.com/camsong/blog/issues/2

http://www.tuicool.com/articles/M7NRr27   看

http://bubkoo.com/2015/05/08/introduction-to-fetch/  看

https://segmentfault.com/a/1190000006095018#articleHeader14  看

 

免責說明

一、本博客中的文章摘自網上的衆多博客,僅做爲本身知識的補充和整理,並分享給其餘須要的coder,不會用於商用。

二、由於不少博客的地址看完沒有及時作保存,因此不少不會在這裏標明出處,很是感謝各位大牛的分享,也但願你們理解。

相關文章
相關標籤/搜索