Ajax 之戰:XMLHttpRequest與Fetch API

作者 | Craig Buckler

譯者 | Phoenix

策劃 | 閆園園

本文最初發佈於 OpenReplay 部落格,由 InfoQ 中文站翻譯並分享。

Ajax 是大多數 web 應用程式背後的核心技術,它允許頁面向 web 服務發出非同步請求,因此資料可以不經過頁面往返伺服器無重新整理顯示資料。

術語 Ajax 不是一種技術,相反,它指的是從客戶端指令碼載入伺服器資料的方法。多年來已經引入了幾種選擇,目前有兩種主要方法,大多數 JavaScript 框架使用其中一種或兩種。

在本文中,我們將研究早期 XMLHttpRequest 和現代 Fetch 的優缺點,以確定哪種 Ajax API 最適合你的應用。

XMLHttpRequest

XMLHttpRequest 在 1999 年首次作為非標準的 Internet Explorer 5。0 ActiveX 元件出現,微軟開發它是為了支援基於瀏覽器的 Outlook 版本,XML 是當時最流行(或被宣揚)的資料格式,除此之外,XMLHttpRequest 還支援文字和尚未發明的 JSON。

Jesse James Garrett 在他 2005 年的文章《AJAX: Web 應用程式的新方法》中提出了“AJAX”概念,那時谷歌郵箱和谷歌地圖等基於 AJAX 的應用程式已經存在,但是這個術語激勵了開發人員,並引起了流暢的 Web 2。0 體驗爆炸式增長。

AJAX 是“Asynchronous JavaScript and XML”的縮寫,儘管嚴格地說,開發人員並不需要使用非同步方法、JavaScript 或 XML。我們現在將通用的“Ajax”術語表示任何從伺服器獲取資料、更新 DOM 而無需重新整理整個頁面的客戶端過程。

所有主流瀏覽器都支援 XMLHttpRequest,並在 2006 年成為官方的 web 標準。下面是一個簡單的例子,從你的域 / 服務 / 端點獲取資料,然後在控制檯將 JSON 結果顯示為文字:

onreadystatechange 回撥函式在請求的生命週期中執行好幾次;XMLHttpRequest 物件的 readyState 屬性則返回當前狀態:

0 (uninitialized) - 請求未初始化

1(loading)- 伺服器連線建立

2(loaded)- 請求收到

3(interactive)- 處理請求

4(complete)- 請求完成,響應準備就緒

在達到狀態 4 之前,幾個函式就可以做很多事情。

Fetch

Fetch 是一個現代基於 promise 的 Ajax 請求 API,首次出現於 2015 年,在大多數瀏覽器中都得到了支援。它不是基於 XMLHttpRequest 構建的,並且用更簡潔的語法提供了更好的一致性。下面的 Promise 鏈函式與上面的 XMLHttpRequest 例子相同:

或者你可以使用 async/await:

Fetch 更清晰、更簡潔,並且經常在 Service worker 中使用。

開源會話重播

OpenReplay 是 FullStory 和 LogRocket 的開源替代品,它透過回放使用者在你的應用程式上的一切操作,並顯示每個問題的操作堆疊,提供完整的可觀察性。OpenReplay 是自託管的,可以完全控制你的資料。

Ajax 之戰:XMLHttpRequest與Fetch API

快樂除錯吧!現代的前端團隊 —— 開始自由地監控你的 web 應用程式。

第 1 回合:Fetch 獲勝

與陳舊的 XMLHttpRequest 相比,Fetch API 除了具有更清晰簡潔的語法之外,還有其它幾個優勢。

頭、請求和響應物件

上面簡單 fetch() 示例中,使用一個字串定義 URL 端點,也可以傳遞一個可配置的 Request 物件,它提供了有關呼叫的一系列屬性:

Response 物件提供了對訪問所有詳細資訊的類似訪問:

Headers 物件提供了一個簡單的介面來設定請求中的頭資訊或獲取響應中的頭資訊:

快取控制

在 XMLHttpRequest 中管理快取具有挑戰性,你可能會發現有必要附加一個隨機查詢字串值來繞過瀏覽器快取,Fetch 方法在第二個引數 init 物件中內建了對快取的支援:

快取可以設定為:

‘default’ —— 如果有一個新的 (未過期的) 匹配,則使用瀏覽器快取;如果沒有,瀏覽器會發出一個帶條件的請求來檢查資源是否已改變,並在必要時會發出新的請求

‘no-store’ —— 繞過瀏覽器快取,並且網路響應不會更新它

‘reload’ —— 繞過瀏覽器快取,但是網路響應會更新它

‘no-cache’ —— 類似於‘default’,除了一個條件請求總是被做

‘force-cache’ —— 如果可能,使用快取的版本,即使它過時了

‘only-if-cached’ —— 相同的 force-cache,除了沒有網路請求

跨域控制

跨域共享資源允許客戶端指令碼向另一個域發出 Ajax 請求,前提是該伺服器允許 Access-Control-Allow-Origin 響應頭中的源域;如果沒有設定這個引數, fetch() 和 XMLHttpRequest 都會失敗。但是,Fetch 提供了一個模式屬性,可以在第二個引數的 init 物件中設定‘no-cors’屬性。

這將返回一個不能讀取但可以被其它的 API 使用的響應。例如,你可以使用 Cache API 儲存返回再之後使用,可能從 Service Worker 返回一個影象、指令碼或 CSS 檔案。

憑證控制

XMLHttpRequest 總是傳送瀏覽器 cookie,Fetch API 不會發送 cookie,除非你顯式地在第二個引數 init 物件中設定 credentials 屬性。

credentials 可以設定為:

‘omit’ —— 排除 cookie 和 HTTP 認證項 (預設)

‘same-origin’ —— 包含對同源 url 的請求的憑證

‘include’ —— 包含所有請求的憑證

請注意,include 是早期 API 實現中的預設值,如果你的使用者可能執行舊的瀏覽器,就得顯式地設定 credentials 屬性。

重定向控制

預設情況下,fetch() 和 XMLHttpRequest 都遵循伺服器重定向。但是,fetch() 在第二個引數 init 物件中提供了替代選項:

redirect 可以設定為:

‘follow’ —— 遵循所有重定向(預設)

‘error’ —— 發生重定向時中止(拒絕)

‘manual’ —— 返回手動處理的響應

資料流

XMLHttpRequest 將整個響應讀入記憶體緩衝區,但是 fetch() 可以流式傳輸請求和響應資料,這是一項新技術,流允許你在傳送或接收時處理更小的資料塊。例如,你可以在完全下載前處理數兆位元組檔案中的資訊,下面的示例將傳入的(二進位制)資料塊轉換為文字,並將其輸出到控制檯。在較慢的連線上,你會看到更小的資料塊在較長的時間內到達。

伺服器端支援

Deno 和 Node 18 中完全支援 Fetch,在伺服器和客戶端使用相同的 API 有助於減少認知成本,還提供了在任何地方執行的同構 JavaScript 庫的可能性。

第二輪:XMLHttpRequest 獲勝

儘管存在缺陷,XMLHttpRequest 還是有一些技巧可以超越 ajax Fetch()。

進度支援

我們可以監控請求的進度,透過將一個處理程式附加到 XMLHttpRequest 物件的進度事件上。這在上傳大檔案(如照片)時特別有用:

事件處理程式傳遞的物件有三個屬性:

lengthComputable —— 如果進度可以計算,則設定為 true

total —— 訊息體的工作總量或內容長度

loaded —— 到目前為止完成的工作或內容的數量

Fetch API 沒有提供任何方法來監控上傳進度。

超時支援

XMLHttpRequest 物件提供了一個 timeout 屬性,可以將其設定為請求自動終止前允許執行的毫秒數;如果超時,就觸發一個 timeout 事件來處理:

fetch() 中可以封裝一個函式來實現超時功能:

或者,你可以使用 Promise。race():

這兩個方法都不容易使用,另外請求將在後臺繼續執行。

中止支援

執行中的請求可以透過 XMLHttpRequest 的 abort() 方法取消,如有必要,可以附加一個 abort 事件來處理:

你可以中止一個 fetch(),但它不是那麼直接,需要一個 AbortController 物件:

當 fetch() 中止時,catch() 塊執行。

更顯式的故障檢測

當開發人員第一次使用 fetch() 時,假設一個 HTTP 錯誤,如 404 Not Found 或 500 Internal Server error 將觸發 Promise 拒絕並執行相關的 catch() 塊,這似乎是合乎邏輯的,但事實並非如此:Promise 成功地解決了這些響應,只有當網路沒有響應或請求被中斷時,才會發生拒絕。

fetch() 的 Response 物件提供了 status 和 ok 屬性,但並不總是顯式地需要檢查它們,XMLHttpRequest 更明確,因為單個回撥函式處理每一個結果:你應該在每個示例中都看到 stuatus 檢查。

瀏覽器支援

我希望你不必支援 Internet Explorer 或 2015 年之前的瀏覽器版本,但如果是這樣的話,XMLHttpRequest 是你唯一的選擇。XMLHttpRequest 也很穩定的,API 不太可能更新。Fetch 比較新,還缺少幾個關鍵特性,雖然更新不太可能破壞程式碼,但你可以期待一些維護。

應該使用哪個 API ?

大多數開發人員都會使用更新的 Fetch API,它的語法更簡潔,比 XMLHttpRequest 更有優勢;也就是說,這些好處中的許多都有特定的用例,但在大多數應用程式中都不需要它們。只有兩種情況下 XMLHttpRequest 仍必不可少:

你正在支援非常老的瀏覽器——這種需求會隨著時間的推移而下降。

你需要顯示上傳進度條。Fetch 後續將會支援,但可能需要幾年的時間。

這兩種選擇都很有趣,值得詳細瞭解它們!

https://blog。openreplay。com/ajax-battle-xmlhttprequest-vs-the-fetch-api

TAG: XMLHttpRequest請求Fetchapi瀏覽器