網路請求發展介紹

XFY發表於2019-01-18

本文講解瀏覽器請求的發展歷程,從傳統web應用到ajax,再到jquery,直到目前最為流行的axios。(重點詳細講,沒用過的寫個思想)

一、傳統web應用

傳統的網頁(不使用 AJAX)如果需要更新內容,必需過載整個網頁。過程是使用者在客戶端觸發HTTP請求,伺服器處理請求並返回新的HTHL頁到客戶端,客戶端再重新讀取整個頁面。即使很小的互動,也要完成上述過程,導致浪費頻寬且互動響應很慢。

在這個背景下,出現了可以更新部分網頁資料的Ajax技術。

二、Ajax

1. 概念

AJAX即 Asynchronous JavaScript and XML。它不是新的程式語言,而是在不重新載入整個頁面的情況下,與服務端交換資料並更新部分網頁的技術。

2. 原理

Ajax 的工作原理相當於在使用者和伺服器之間加了—箇中間層,使使用者操作與伺服器響應非同步化,並且確定需要從伺服器讀取新資料時再由Ajax引擎代為向伺服器提交請求,像—些資料驗證和資料處理等都交給Ajax引擎自己來做,並不提交給服務端,提升效能。

3. 簡單示例

Ajax 核心由 XMLHTTPRequest、JavaScript、DOM 物件組成,通過XmlHttpRequest物件來向伺服器發非同步請求,從伺服器獲得資料,然後用 JavaScript 來操作 DOM 而更新頁面。

const xmlHttp = new XMLHttpRequest()
// 傳送請求
xmlHttp.open('GET', 'text1.txt', true) // open(method,url,async)
xmlHttp.send()

// 響應處理
xmlHttp.onreadystatechange = function () {
  // 請求已完成,且響應就緒
  if (xmlHttp.readyState === 4  && xmlHttp.status === 200) {
    document.getElementById('div1').innerHTML = xmlHttp.responsText
  }
}
複製程式碼

4. XMLHttpRequest物件

  • readyState:存有 XMLHttpRequest 的狀態。從 0 到 4 發生變化。改變時觸發onreadystatechange
    • 0: 請求未初始化
    • 1: 伺服器連線已建立
    • 2: 請求已接收
    • 3: 請求處理中
    • 4: 請求已完成,且響應已就緒
  • status: 伺服器的HTTP狀態碼
    • 200: "OK"
    • 404: 未找到頁面
    • ...

5. 優缺點

  • 優點:

    • 無重新整理更新資料,使用者體驗提高
    • 按需取資料,節約空間和頻寬
    • 部分工作轉嫁到客戶端,減輕伺服器負擔
    • 基於標準被支援
  • 缺點:

    • 客戶端變的複雜,容易出錯
    • 移動裝置支援較差
    • 安全問題

三、Jquery

1. 背景

因為不同的瀏覽器對 AJAX 的實現並不相同,所以編寫統一的 AJAX 程式碼相對複雜。例如IE5、IE6沒有XMLHttpRequest物件,而是ActiveXObject。

Jqury 庫對 AJAX 進行了封裝,使得我們可以通過更為簡單統一的方式實現 AJAX 功能,同時能夠把這些外部資料直接載入網頁的被選元素中。

2. 主要 API 介紹

  • $(selector).load(URL, data, callback): 從服務端獲取獲取並將資料載入到DOM元素中
  • $.get(URL,callback) : 使用 GET 方法從服務端獲取資料
  • $.post( url [, data ] [, success ] [, dataType ] ) : 通過 HTTP POST 請求從伺服器上請求資料。
  • api詳情

四、Fetch

1. Why fetch

傳統ajax(指的是 XmlHttpRequest,即XHR)已經逐漸被Fetch替代。原因:

  • Fetch語法簡單,更加語義化, 並能方便的配置請求物件。
  • 不需要引入jquery,且移動端支援較好

2. 簡單示例

fetch(url).then(function(response) {
  return response.json();
}).then(function(data) {
  console.log(data);
}).catch(function(e) {
  console.log("Oops, error");
});

// 使用 ES6 的 箭頭函式 後:
fetch(url).then(response => response.json())
  .then(data => console.log(data))
  .catch(e => console.log("Oops, error", e))
複製程式碼

3. polyfill: whatwg-fetch

fetch的原生支援並不好,通過引入polyfill來支援主流瀏覽器。實質是對於不支援 fetch api 的瀏覽器,使用 XMLHttpRequest 模擬實現 fetch api。

  • 關鍵原始碼:

    if (self.fetch) {
      return
    }
    ...
    self.fetch = function(input, init) {
        return new Promise(function(resolve, reject) {
          var request = new Request(input, init)
          var xhr = new XMLHttpRequest()
    
          xhr.onload = function() {...}
        }
    }
    複製程式碼
  • plato((專案採用的vue腳手架)引入方法:

    // src/polyfills/index
    import 'whatwg-fetch'
    
    // webpack.config.bable.js
    entry: {
      app: [
        paths.src('polyfills/index.js'),  // 載入 polyfills
        paths.src('index.js')]  // 載入入口
    }
    複製程式碼
  • 和Jquery.ajax()的區別

    • 伺服器返回 404,500 錯誤碼時並不會reject,只有網路錯誤或者其他原因導致請求不能完成時,fetch 才會被 reject。為了保證reject HTTP錯誤狀態,可做如下處理:eg:所有非2xx的錯誤碼都丟擲錯誤異常。 (----- 不好用,有時候需要做處理)

      function checkStatus(response) {
        if (response.status >= 200 && response.status < 300) {
          return response
        } else {
          var error = new Error(response.statusText)
          error.response = response
          throw error
        }
      }
      
      function parseJSON(response) {
        return response.json()
      }
      
      fetch('/users')
        .then(checkStatus)
        .then(parseJSON)
        .then(function(data) {
          console.log('request succeeded with JSON response', data)
        }).catch(function(error) {
          console.log('request failed', error)
        })
      複製程式碼
    • plato(專案採用的vue腳手架)實現建request.js

      const body = await getBody(res)
      if (res.status >= 200 && res.status < 400) {
        return body
      } else {
        throw body
      }
      
      async function getBody (res) {
        const type = res.headers.get('Content-Type')
      
        if (type && type.indexOf('json') !== -1) {
          return await res.json()
        }
      
        const body = await res.text()
      
        try {
          return JSON.parse(body)
        } catch (error) {
          return { body }
        }
      }
      複製程式碼
  • Fetch 請求預設是不帶 cookie 的,而且也不接收cookie。

    • 傳送cookie

      • 不跨域:fetch(url, {credentials: 'same-origin'})
      • CORS 跨域請求:fetch(url, {credentials: 'include'})
    • 接收cookie
      同XHR相同, 從服務端返回的響應頭Set-Cookie是一個被禁的欄位,所以不能通過response.headers.get()來獲取。獲取cookie應該是瀏覽器的職責,通過document.cookie來實現。

4. fetch() 詳解

  • 語法: Promise fetch(input[, init])
  • 引數
    • input:定義要獲取的資源
    • init: 一個配置項物件,包括所有對請求的設定。可選的引數有:
      • method
      • headers
      • body
      • mode
      • credientials
      • cache
      • ...
  • 返回值: 一個 Promise,resolve 時回傳 Response 物件。

五、Axios

基於Promise的HTTP庫,用於瀏覽器端和Node.js端, 底層實現也是封裝XMLHttpRequest。

  • Why axios:

    • 支援兩端:browser, nodejs
    • 支援攔截器
    • 支援json資料自動轉換
    • 基於promise,使用簡單
    • 體積小
    • fetch 實現偏底層,通常需要封裝後使用,axios可直接用
  • 特點

    • 瀏覽器端:make XMLHttpRequest
    • Nodejs:make http request
    • 支援promise api
    • 支援請求 intercept、響應intercept
    • 自動轉換JSON資料

參考:

五、參考

相關文章