天天看點

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 是自托管的,可以完全控制你的資料。

快樂調試吧!現代的前端團隊 —— 開始自由地監控你的 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

繼續閱讀