JS 中的網路請求 AJAX, Fetch, WebSocket

wopen發表於2019-03-02

AJAX 是 Asynchronous JavaScript And XML 的簡稱,它可以讓頁面在不重新整理的情況下從伺服器獲取資料。

XMLHttpRequest

瀏覽器使用XMLHttpRequest物件於伺服器通訊,它可以使用JSON,XML,HTML和text等格式傳送和接收資料。

低版本 IE 瀏覽器沒有XMLHttpRequest物件,但是它可以使用ActiveXObject物件代替。

if (window.XMLHttpRequest) { // IE7+
    XHR = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 6
    XHR = new ActiveXObject("Microsoft.XMLHTTP");
}
複製程式碼

建立 XHR 例項過後就可以監聽該例項的狀態改變事件onreadystatechange,它會在 XHR 例項的readyState的值改變時觸發回撥函式。

XHR.onreadystatechange = function () { }
複製程式碼

然後我們就可以使用open方法初始化一個請求和send方法傳送 HTTP 請求。

XHR.open('GET', 'http://q.com')

// open 方法一共有 5 個引數,method, url, async, user, password 後三個可選。
// async 表示這次是否非同步請求,預設是 true

XHR.send()
// send 方法接受一個可選引數 請求主體。
// 引數可以是 FormData, FormData, ArrayBuffer, Document, 序列化字串
複製程式碼

如果是post方法,就要在send之前設定請求頭的Content-Type

httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')

// 它接受兩個引數 header 和 value
複製程式碼

然後我們就要處理伺服器返回的資料,回到onreadystatechange函式。它監聽readyState屬性的變化,而它一共有 5 個值。

  1. 0 表示 請求還未初始化,尚未呼叫 open() 方法。
  2. 1 表示 已建立伺服器連結,open() 方法已經被呼叫。
  3. 2 表示 請求已接受,send() 方法已經被呼叫,並且頭部和狀態已經可獲得。
  4. 3 表示 正在處理請求,下載中; responseText 屬性已經包含部分資料。
  5. 4 表示 完成,下載操作已完成。

我們還需要關心status屬性它也是隻讀屬性,它是這次響應中的 HTTP 數字狀態碼。在請求之前和 XMLHttpRequest 出錯時它為0

responseText 屬性是實際的資料,它是字串,如果相應是 JSON 格式,需要用 JSON 的 parse 處理。

XHR.onreadystatechange = function () {
    if (XHR.readyState === 4 && XHR.status === 200) {
        console.log(JSON.parse(XHR.responseText))
    }
}
複製程式碼

如果伺服器返回的是 XML, 我們可以用responseXML屬性獲得資料。

// 如果已指明,responseType 必須是空字串或 "docuemnt" 
XHR.responseType = 'document';

// overrideMimeType() 用來強制解析 response 為 XML
XHR.overrideMimeType('text/xml');

// --------------

var root = XHR.responseXML.getElementsByTagName('root').item(0)
複製程式碼

responseType 屬性是一個列舉型別的屬性,返回響應資料的型別。

withCredentials 屬性是一個Boolean型別,它指示了是否該使用類似cookies,authorization headers(頭部授權)或者TLS客戶端證照這一類資格證照來建立一個跨站點訪問控制請求。

(在IE中,超時屬性可能只能在呼叫 open() 方法之後且在呼叫 send() 方法之前設定)

abort方法用來終止請求

getAllResponseHeaders方法返回所有的響應頭

getResponseHeader(name)方法返回包含指定頭文字的字串

XMLHttpRequset 2

XMLHttpRequset 2 增加了一些新功能。比如能傳送FormData

超時時間

timeout 屬性是超時時間,單位毫秒。當超時發生時他會觸發ontimeout回撥函式。

xhr.open('GET', '/server')

xhr.timeout = 2000

xhr.ontimeout = function (e) {}

xhr.send(null);
複製程式碼

還有 6 個進度事件。

  1. loadstart 在收到響應的第一個位元組觸發
  2. progress 在接收期間不斷觸發
  3. error 發生錯誤
  4. abort 呼叫abort方法而終止
  5. load 接收到完整資料
  6. loadend 在通訊完成或abort error load事件後觸發

load事件就不用readystatechange事件和讀取readyState屬性。

xhr.addEventListener("progress", updateProgress, false);
xhr.addEventListener("load", transferComplete, false);
xhr.addEventListener("error", transferFailed, false);
xhr.addEventListener("abort", transferCanceled, false);

function updateProgress(event) {
  if (event.lengthComputable) {
    console.log(`${event.position} / ${event.totalSize}`)
  }
}
複製程式碼

其中progress的事件物件多了三個屬性。

  1. lengthComputable 布林值 表示進度資訊是否可用
  2. position 已經接收到的位元組數
  3. totalSize 根據Content-Length預期的位元組數

跨域

同源策略限制了從同一個源載入的文件或指令碼如何與來自另一個源的資源進行互動。這是一個用於隔離潛在惡意檔案的重要安全機制。

當前網址(http://news.a.com)和請求網址

  1. https://news.a.com 不同協議
  2. http://news.a.com:8080 不同埠
  3. http://home.a.com 不同域名

時就不是同源。

為了使 ajax 可以從不同的網址獲取資料。

我們可以使用跨域資源共享(CORS)來解決問題。

在傳送請求時會有個Origin頭表示請求頁面的源資訊, 如果伺服器返回的Access-Control-Allow-Origin中有相同的源資訊或是* 那麼就可以跨域請求資訊,請求和響應都不包含cookie

CORS通過Preflighted Requests透明伺服器驗證機制支援使用自定義頭部、get和post之外的方法,不同型別的主題內容。

這種請求已OPTIONS方法傳送,下面是它傳送的頭資訊:

Origin

Access-Control-Request-Method 請求自身使用的方法

Access-Control-Request-Headers 自定義頭部資訊,用逗號分隔

傳送請求後,伺服器來決定是否允許,伺服器會傳送如下資訊與瀏覽器溝通:

Access-Control-Allow-Origin 允許的源

Access-Control-Allow-Methods 允許的方法,逗號分隔

Access-Control-Allow-Headers 允許的頭部,逗號分隔

Access-Control-Allow-Max-Age Preflight請求快取的時間(秒)

預設情況下跨域不提供cookie、HTTP認證、SSL證明,通過withCredentials屬性設定為true可以指定某個請求因該傳送憑據。 伺服器如果接收請求會返回Access-Control-Allow-Credentialstrue的頭資訊。

還有一種方法是使用JSONP

jsonp方法主要是建立script標籤來獲得資料,一般通過請求後面跟?callback=fn 回掉函式來獲取資料。

Fetch

Fetch 是網路請求的一個更好的替代方法。相比於 XMLHttpRequest,Fetch 寫法更簡單,功能更強大。

fetch('http://a.com')
  .then(function(response) {
    if (response.ok) {
        return response.json();
    }
    throw new Error('err')
  })
  .then(function(myJson) {
    console.log(myJson);
  })
  .catch(err => {
      console.log(err)
  })
複製程式碼

fetch 函式接受兩個引數,返回一個 Promise 物件

第一個引數是 URL 或 Request 物件。第二個引數是可選一個配置項物件。

{
    method: 'GET', // 請求方法
    headers: {
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    }, // 頭資訊
    body: JSON.stringify({data: 1}), // 請求的 body 資訊,Blob, FormData 等
    mode: 'cors', // 請求的模式,cors、 no-cors 或 same-origin
    credentials: 'include', // omit、same-origin 或 include。為了在當前域名內自動傳送 cookie, 必須提供這個選項
    cache: 'no-cache', // default 、 no-store 、 reload 、 no-cache 、 force-cache 或者 only-if-cached
    redirect: 'follow', // 可用的 redirect 模式: follow (自動重定向), error (如果產生重定向將自動終止並且丟擲一個錯誤), 或者 manual (手動處理重定向).
    referrer: 'no-referrer', // no-referrer、client或一個 URL。預設是 client。
    referrerPolicy: 'no-referrer', // 指定 referer HTTP頭
    integrity: 'sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=', // 包括請求的  subresource integrity 值
}
複製程式碼

then 的回撥函式接受一個 Response 物件。Response 實現了 Body(代表響應/請求的正文,允許你宣告其內容型別是什麼以及應該如何處理。)

它有 9 個屬性。

  1. type 只讀 包含Response的型別 (例如, basic, cors)
  2. url 只讀 包含Response的URL
  3. useFinalURL 包含了一個布林值來標示這是否是該Response的最終URL
  4. status 只讀 包含Response的狀態碼
  5. ok 只讀 包含了一個布林值來標示該Response成功(狀態碼200-299)
  6. edirected 只讀 表示該Response是否來自一個重定向,如果是的話,它的URL列表將會有多個
  7. statusText 只讀 包含了與該Response狀態碼一致的狀態資訊
  8. headers 只讀 包含此Response所關聯的Headers 物件
  9. bodyUsed Body 只讀 包含了一個布林值來標示該Response是否讀取過Body

8 個方法

  1. clone 建立一個Response物件的克隆
  2. error 返回一個繫結了網路錯誤的新的Response物件
  3. redirect(url, status) 用另一個URL建立一個新的 response

Body(都返回一個 Promise 例項)

  1. arrayBuffer 接受一個 Response 流, 並等待其讀取完成. 並 resolve 一個 ArrayBuffer 物件
  2. blob blob()方法使用一個 Response 流,並將其讀取完成
  3. formData 將 Response 物件中的所承載的資料流讀取並封裝成為一個物件
  4. json 使用一個 Response 流,並將其讀取完成。解析結果是將文字體解析為 JSON
  5. text 提供了一個可供讀取的"返回流", 它返回一個包含USVString物件,編碼為UTF-8

WebSocket

WebSockets 是一種先進的技術。它可以在使用者的瀏覽器和伺服器之間開啟雙工、雙向通訊會話。

WebSocket 建構函式,接受兩個引數,url 和 protocols(可選)。

url 以 ws://wss://(加密)開頭

protocols 是 單協議字串或者包含協議字串的陣列。這些字串用於指定子協議,這樣單個伺服器可以實現多個WebSocket子協議(例如,您可能希望一臺伺服器能夠根據指定的協議處理不同型別的互動)protocol)。如果不指定協議字串,則假定為空字串。

var s = new WebSocket('ws://www.a.com/s.php') // 必須傳入絕對URL,可以是任何網站
s.readyState // 0 建立連線 1 已經建立 2 正在關閉 3 連線已關閉或者沒有連結成功
s.send('hello') // 傳送的資料必須是純文字
s.onopen = function (){
  console.log('成功建立連線時觸發')
}
s.onerror = function () {
  console.log('發生錯誤,連線不能持續時')
}
s.onmessage = function (event) { // 當接收到訊息時
  console.log(event.data) // 資料是純字元
}
s.close() // 關閉連線
s.onclose = function (event) {
  /*
   * event.wasClean 是否明確的關閉 
   * event.code 伺服器返回的數值狀態碼
   * event.reason 字串,伺服器返回的訊息
   */
  console.log('連線關閉時')
}
複製程式碼

一共有 10 個屬性

  1. binaryType 返回websocket連線所傳輸二進位制資料的型別(blob, arraybuffer)
  2. bufferedAmount 只讀 返回已經被send()方法放入佇列中但還沒有被髮送到網路中的資料的位元組數。一旦佇列中的所有資料被髮送至網路,則該屬性值將被重置為0。但是,若在傳送過程中連線被關閉,則屬性值不會重置為0。
  3. extensions 只讀 返回伺服器選擇的副檔名。這當前只是空字串或連線協商的擴充套件列表
  4. onclose 用於指定連線失敗後的回撥函式
  5. onmessage 用於指定當從伺服器接受到資訊時的回撥函式
  6. onopen 用於指定連線成功後的回撥函式
  7. protocol 只讀 伺服器選擇的下屬協議
  8. readyState 只讀 當前的連結狀態
  9. url 只讀 WebSocket 的絕對路徑

2 個方法

  1. close(code, reason) 數字狀態碼 可選 預設 1005和一個可選的類可讀的字串,它解釋了連線關閉的原因。
  2. send(data) 向伺服器傳送資料(ArrayBuffer,Blob等)

相關文章