前端 fetch 通訊

本文摘至: please call me HRphp

隨着前端異步的發展, XHR 這種耦合方式的書寫不利於前端異步的 Promise 回調. 並且,寫起來也是很複雜. fetch API 原本是在 SW(ServiceWorkers) 中提出的, 不過, 後面以爲好用, 就把他掛載到 window 對象下. 這樣, 在前端的正常通訊中, 咱們也能夠直接調用. 但, fetch 畢竟比較新, 看一下他的兼容性.html

fetch compatiable

在 PC 端上, 就 FF, Opera 和 Chrome 比較 fashion. mobile 的話, 基本上是不能用的. 固然, 前端一直是個擁抱變化的職業, 官方已經有一個現成的 polyfill 可使用. 這樣的話, 就不必過多擔憂. 前端

每用到一個新的 feature, 咱們首先得知道他能不能用. Modernizr 這個庫作的挺好的. 這裏, 咱們簡單的瞭解一下就 ok 了.jquery

let isFetch = window.fetch?true:false;

fetch 基本格式

能夠說, fetch 就是 ajax + Promise. 使用的方式和 jquery 提供的 $.ajax() 差很少.git

fetch('./api/some.json')  
  .then(  
    function(response) {  
      if (response.status !== 200) {  
        console.log(`返回的響應碼${response.status}`);  
        return;  
      }

      // 得到後臺實際返回的內容
      response.json().then(function(data) {  
        console.log(data);  
      });  
    }  
  )  
  .catch(function(err) {  
    console.log('Fetch Error :-S', err);  
  });

上面的 demo 很好的參數了, fetch 的幾個特色.github

  • then()web

  • catch()ajax

  • json()json

then 和 catch 是 promise 自帶的兩個方法, 我這裏就很少說了. 咱們來看一下,json 是幹嗎的. api

由於返回回來的數據不只僅包括後臺的返回的 Text 內容, 還有一些 Headers. 因此在, then 方法裏面返回來的 res 實際上並非咱們在業務中想要的內容. 就和在 XHR 裏返回數據是一個道理, 咱們最終要的是 responseText 這個數據. 而 json() 方法實際作的事情,就是調用 JSON.parse() 處理數據, 而且返回一個新的 Promise. 看一下 polyfill 源碼就應該瞭解.

this.json = function() {
    return this.text().then(JSON.parse)
}

fetch 傳輸格式

上面的 demo 是一個 get 方法的請求, 固然, 除了 get , 還有其餘的 HTTP Method, PUT, DELETE, POST, PATCH 等. 這裏, 咱們就說一個 POST, 其餘方法的基本格式仍是相似的.

fetch("http://www.example.org/submit.php", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: "this is a post Msg"
}).then(function(res) {
  if (res.ok) {
    // doSth
  } else if (res.status == 401) {
    // doSth
  }
});

看起來 fetch 和 $.ajax 並無多大的區別...
but... fetch 裏面的內容,真很多. 往底層看一看, fetch 其實是 Request,Headers,Response 3個接口的整合. 不過, 這3個只能在 SW 裏面使用. 這裏當作原理,參數一下便可.

Headers 操做

Headers 的操做無非就是 CRUD, 這裏我就不過多贅述,直接看代碼吧:

var content = "Hello World";
var reqHeaders = new Headers();
reqHeaders.append("Content-Type", "text/plain"
reqHeaders.append("Content-Length", content.length.toString());
reqHeaders.append("X-Custom-Header", "自定義頭");

固然, 你也可使用字面量的形式:

reqHeaders = new Headers({
  "Content-Type": "text/plain",
  "Content-Length": content.length.toString(),
  "X-Custom-Header": "自定義頭",
});

接下來就是, 頭部的內容的檢測相關.

console.log(reqHeaders.has("Content-Type")); // true
console.log(reqHeaders.has("Set-Cookie")); // false
reqHeaders.set("Content-Type", "text/html");
reqHeaders.append("X-Custom-Header", "新增自定義頭");
 
console.log(reqHeaders.get("Content-Length")); // 11
console.log(reqHeaders.getAll("X-Custom-Header")); // ["自定義頭", "新增自定義頭"]
 
reqHeaders.delete("X-Custom-Header");
console.log(reqHeaders.getAll("X-Custom-Header")); // []

不過, 鑑於安全性的考慮, 有時候在多人協做或者子版塊管理時, 對於頭部的限制仍是須要的. 這裏, 咱們能夠經過 guard 屬性, 設置 Headers 的相關策略.
guard 一般能夠取 "immutable", "request", "request-no-cors", "response", "none".
咱們這裏不探討所有, 僅僅看一下 request 這個選項.
當你設置了 request 以後, 若是你設置的 Header 涉及到 forbidden header name (這個是瀏覽器自動設置的), 那麼該次操做是不會成功的.
forbidden header name 一般有.

  • Accept-Charset

  • Accept-Encoding

  • Access-Control-Request-Headers

  • Access-Control-Request-Method

  • Connection

  • Content-Length

  • Cookie

  • Cookie2

  • Date

  • DNT

  • Expect

  • Host

  • Keep-Alive

  • Origin

  • Referer

  • TE

  • Trailer

  • Transfer-Encoding

  • Upgrade

  • Via

對比與 fetch, 咱們沒有辦法去設置 Headers的 guard, 因此, 這隻能在 SW 裏使用.

Request 操做

Request 的基本用法和 fetch 差很少.

var uploadReq = new Request("/uploadImage", {
  method: "POST",
  headers: {
    "Content-Type": "image/png",
  },
  body: "image data"
});

fetch("/uploadImage", {
  method: "POST",
  headers: {
    "Content-Type": "image/png",
  },
  body: "image data"
});

在瀏覽器裏, 一切請求都逃不過跨域和不跨域的問題. fetch 也是. 對於跨域的請求, 主要的影響仍是體如今 Response 中, 這 fetch Request 這, 沒多大影響. 不過, 咱們須要在 fetch 設置 mode 屬性, 來表示這是一個跨域的請求.

fetch('https://www.villainhr.com/cors-enabled/some.json', {mode: 'cors'})  
  .then(function(response) {  
    return response.text();  
  })

經常使用的 mode 屬性值有:

  • same-origin: 表示只請求同域. 若是你在該 mode 下進行的是跨域的請求的話, 那麼就會報錯.

  • no-cors: 正常的網絡請求, 主要應對於沒有後臺沒有設置 Access-Control-Allow-Origin. 話句話說, 就是用來處理 script, image 等的請求的. 他是 mode 的默認值.

  • cors: 用來發送跨域的請求. 在發送請求時, 須要帶上.

另外, 還有一個關於 cookie 的跨域內容. 在 XHR2 中,咱們瞭解到, withCredentials 這個屬性就是用來設置在進行跨域操做時, 對不一樣域的 Server 是否發送本域的 cookie. 通常設置爲 omit(不發送). 在 fetch 當中, 使用的是 credentials 屬性.
credentials 經常使用取值爲:

  • omit: 發送請求時,不帶上 cookie. 默認值.

  • same-origin: 發送同域請求時,會帶上 cookie.

  • include: 只要發送請求,都會帶上 cookie.

因此, 若是你想發送 ajax 時, 帶上 cookie, 那麼你就須要使用 same-origin, 若是想在跨域時也帶上 cookie, 那麼就須要 include.

// 跨域請求
fetch('https://www.villainhr.com/cors-enabled/some.json', {mode: 'cors',credentials:'include'})  
  .then(function(response) {  
    return response.text();  
  })

Response 操做

response 應該算和 fetch 最爲接近的一個對象. Response 的實際其實就是 fetch 回調函數傳回的參數. Response 中比較經常使用的屬性有四個: status, statusText, ok, type.

  • status: 返回的狀態碼. 100~500+

  • statusText: 返回狀態碼錶明的含義. 好比, 返回"ok".

  • ok: 用來檢差 status 是否在200和299之間.

  • type: 表示請求是否跨域, 或是否出錯. 取值爲: 「basic」, 「cors」, 「default」, 「error」 或
    「opaque」.

fetch('https://www.villainhr.com/cors-enabled/some.json', {mode: 'cors',credentials:'include'})  
  .then(function(response) {  
    // ...
  })

這裏, 咱們主要關心一下 type 上面掛載的一些屬性.

  • basic: 同域通訊類別. 能夠正常的訪問 response 的 header(除了 Set-Cookie 頭).

  • cors: 跨域通訊類別. 通常只能訪問如下的頭:

    • Cache-Control

    • Content-Language

    • Content-Type

    • Expires

    • Last-Modified

    • Pragma

  • error: 網絡錯誤類別.

  • opaque: 沒法理解類別. 當使用 no-cors 發送跨域請求時,會觸發.

另外,在 response 上面,還掛載了幾個經常使用的方法: text(),json().

  • text(): 主要用來處理 server 返回的 string 類型數據.

  • josn(): 主要用來處理 server 返回的 json 類型數據.

使用方式都是流式 API.

fetch('https://www.villainhr.com/cors-enabled/some.json')  
  .then(function(res) {  
    res.text().then((text)=>{...})
    res.json().then((obj)=>{...})
  })

基本的內容就是上述內容. 若是想更詳細參考的話, 請查閱:

This API is so Fetching!

introduction-to-fetch

相關文章
相關標籤/搜索