XMLHttpRequest與Fetch API
本文最初發(fā)布于 OpenReplay 博客,由 InfoQ 中文站翻譯并分享。
Ajax 是大多數(shù) web 應(yīng)用程序背后的核心技術(shù),它允許頁(yè)面向 web 服務(wù)發(fā)出異步請(qǐng)求,因此數(shù)據(jù)可以不經(jīng)過頁(yè)面往返服務(wù)器無刷新顯示數(shù)據(jù)。
術(shù)語(yǔ) Ajax 不是一種技術(shù),相反,它指的是從客戶端腳本加載服務(wù)器數(shù)據(jù)的方法。多年來已經(jīng)引入了幾種選擇,目前有兩種主要方法,大多數(shù) JavaScript 框架使用其中一種或兩種。
在本文中,我們將研究早期 XMLHttpRequest 和現(xiàn)代 Fetch 的優(yōu)缺點(diǎn),以確定哪種 Ajax API 最適合你的應(yīng)用。
XMLHttpRequest
XMLHttpRequest 在 1999 年首次作為非標(biāo)準(zhǔn)的 Internet Explorer 5.0 ActiveX 組件出現(xiàn),微軟開發(fā)它是為了支持基于瀏覽器的 Outlook 版本,XML 是當(dāng)時(shí)最流行(或被宣揚(yáng))的數(shù)據(jù)格式,除此之外,XMLHttpRequest 還支持文本和尚未發(fā)明的 JSON。
Jesse James Garrett 在他 2005 年的文章《AJAX: Web 應(yīng)用程序的新方法》中提出了“AJAX”概念,那時(shí)谷歌郵箱和谷歌地圖等基于 AJAX 的應(yīng)用程序已經(jīng)存在,但是這個(gè)術(shù)語(yǔ)激勵(lì)了開發(fā)人員,并引起了流暢的 Web 2.0 體驗(yàn)爆炸式增長(zhǎng)。
AJAX 是“Asynchronous JavaScript and XML”的縮寫,盡管嚴(yán)格地說,開發(fā)人員并不需要使用異步方法、JavaScript 或 XML。我們現(xiàn)在將通用的“Ajax”術(shù)語(yǔ)表示任何從服務(wù)器獲取數(shù)據(jù)、更新 DOM 而無需刷新整個(gè)頁(yè)面的客戶端過程。
所有主流瀏覽器都支持 XMLHttpRequest,并在 2006 年成為官方的 web 標(biāo)準(zhǔn)。下面是一個(gè)簡(jiǎn)單的例子,從你的域 / 服務(wù) / 端點(diǎn)獲取數(shù)據(jù),然后在控制臺(tái)將 JSON 結(jié)果顯示為文本:
onreadystatechange 回調(diào)函數(shù)在請(qǐng)求的生命周期中運(yùn)行好幾次;XMLHttpRequest 對(duì)象的 readyState 屬性則返回當(dāng)前狀態(tài):
0 (uninitialized) - 請(qǐng)求未初始化
1(loading)- 服務(wù)器連接建立
2(loaded)- 請(qǐng)求收到
3(interactive)- 處理請(qǐng)求
4(complete)- 請(qǐng)求完成,響應(yīng)準(zhǔn)備就緒
在達(dá)到狀態(tài) 4 之前,幾個(gè)函數(shù)就可以做很多事情。
Fetch
Fetch 是一個(gè)現(xiàn)代基于 promise 的 Ajax 請(qǐng)求 API,首次出現(xiàn)于 2015 年,在大多數(shù)瀏覽器中都得到了支持。它不是基于 XMLHttpRequest 構(gòu)建的,并且用更簡(jiǎn)潔的語(yǔ)法提供了更好的一致性。下面的 Promise 鏈函數(shù)與上面的 XMLHttpRequest 例子相同:
或者你可以使用 async/await:
Fetch 更清晰、更簡(jiǎn)潔,并且經(jīng)常在 Service worker 中使用。
開源會(huì)話重播
OpenReplay 是 FullStory 和 LogRocket 的開源替代品,它通過回放用戶在你的應(yīng)用程序上的一切操作,并顯示每個(gè)問題的操作堆棧,提供完整的可觀察性。OpenReplay 是自托管的,可以完全控制你的數(shù)據(jù)。
快樂調(diào)試吧!現(xiàn)代的前端團(tuán)隊(duì) —— 開始自由地監(jiān)控你的 web 應(yīng)用程序。
第 1 回合:Fetch 獲勝
與陳舊的 XMLHttpRequest 相比,F(xiàn)etch API 除了具有更清晰簡(jiǎn)潔的語(yǔ)法之外,還有其它幾個(gè)優(yōu)勢(shì)。
頭、請(qǐng)求和響應(yīng)對(duì)象
上面簡(jiǎn)單 fetch() 示例中,使用一個(gè)字符串定義 URL 端點(diǎn),也可以傳遞一個(gè)可配置的 Request 對(duì)象,它提供了有關(guān)調(diào)用的一系列屬性:
Response 對(duì)象提供了對(duì)訪問所有詳細(xì)信息的類似訪問:
Headers 對(duì)象提供了一個(gè)簡(jiǎn)單的接口來設(shè)置請(qǐng)求中的頭信息或獲取響應(yīng)中的頭信息:
緩存控制
在 XMLHttpRequest 中管理緩存具有挑戰(zhàn)性,你可能會(huì)發(fā)現(xiàn)有必要附加一個(gè)隨機(jī)查詢字符串值來繞過瀏覽器緩存,F(xiàn)etch 方法在第二個(gè)參數(shù) init 對(duì)象中內(nèi)置了對(duì)緩存的支持:
緩存可以設(shè)置為:
'default' —— 如果有一個(gè)新的 (未過期的) 匹配,則使用瀏覽器緩存;如果沒有,瀏覽器會(huì)發(fā)出一個(gè)帶條件的請(qǐng)求來檢查資源是否已改變,并在必要時(shí)會(huì)發(fā)出新的請(qǐng)求
'no-store' —— 繞過瀏覽器緩存,并且網(wǎng)絡(luò)響應(yīng)不會(huì)更新它
'reload' —— 繞過瀏覽器緩存,但是網(wǎng)絡(luò)響應(yīng)會(huì)更新它
'no-cache' —— 類似于'default',除了一個(gè)條件請(qǐng)求總是被做
'force-cache' —— 如果可能,使用緩存的版本,即使它過時(shí)了
'only-if-cached' —— 相同的 force-cache,除了沒有網(wǎng)絡(luò)請(qǐng)求
跨域控制
跨域共享資源允許客戶端腳本向另一個(gè)域發(fā)出 Ajax 請(qǐng)求,前提是該服務(wù)器允許 Access-Control-Allow-Origin 響應(yīng)頭中的源域;如果沒有設(shè)置這個(gè)參數(shù), fetch() 和 XMLHttpRequest 都會(huì)失敗。但是,F(xiàn)etch 提供了一個(gè)模式屬性,可以在第二個(gè)參數(shù)的 init 對(duì)象中設(shè)置‘no-cors’屬性。
這將返回一個(gè)不能讀取但可以被其它的 API 使用的響應(yīng)。例如,你可以使用 Cache API 存儲(chǔ)返回再之后使用,可能從 Service Worker 返回一個(gè)圖像、腳本或 CSS 文件。
憑證控制
XMLHttpRequest 總是發(fā)送瀏覽器 cookie,F(xiàn)etch API 不會(huì)發(fā)送 cookie,除非你顯式地在第二個(gè)參數(shù) init 對(duì)象中設(shè)置 credentials 屬性。
credentials 可以設(shè)置為:
'omit' —— 排除 cookie 和 HTTP 認(rèn)證項(xiàng) (默認(rèn))
'same-origin' —— 包含對(duì)同源 url 的請(qǐng)求的憑證
'include' —— 包含所有請(qǐng)求的憑證
請(qǐng)注意,include 是早期 API 實(shí)現(xiàn)中的默認(rèn)值,如果你的用戶可能運(yùn)行舊的瀏覽器,就得顯式地設(shè)置 credentials 屬性。
重定向控制
默認(rèn)情況下,fetch() 和 XMLHttpRequest 都遵循服務(wù)器重定向。但是,fetch() 在第二個(gè)參數(shù) init 對(duì)象中提供了替代選項(xiàng):
redirect 可以設(shè)置為:
'follow' —— 遵循所有重定向(默認(rèn))
'error' —— 發(fā)生重定向時(shí)中止(拒絕)
'manual' —— 返回手動(dòng)處理的響應(yīng)
數(shù)據(jù)流
XMLHttpRequest 將整個(gè)響應(yīng)讀入內(nèi)存緩沖區(qū),但是 fetch() 可以流式傳輸請(qǐng)求和響應(yīng)數(shù)據(jù),這是一項(xiàng)新技術(shù),流允許你在發(fā)送或接收時(shí)處理更小的數(shù)據(jù)塊。例如,你可以在完全下載前處理數(shù)兆字節(jié)文件中的信息,下面的示例將傳入的(二進(jìn)制)數(shù)據(jù)塊轉(zhuǎn)換為文本,并將其輸出到控制臺(tái)。在較慢的連接上,你會(huì)看到更小的數(shù)據(jù)塊在較長(zhǎng)的時(shí)間內(nèi)到達(dá)。
服務(wù)器端支持
Deno 和 Node 18 中完全支持 Fetch,在服務(wù)器和客戶端使用相同的 API 有助于減少認(rèn)知成本,還提供了在任何地方運(yùn)行的同構(gòu) JavaScript 庫(kù)的可能性。
第二輪:XMLHttpRequest 獲勝
盡管存在缺陷,XMLHttpRequest 還是有一些技巧可以超越 ajax Fetch()。
進(jìn)度支持
我們可以監(jiān)控請(qǐng)求的進(jìn)度,通過將一個(gè)處理程序附加到 XMLHttpRequest 對(duì)象的進(jìn)度事件上。這在上傳大文件(如照片)時(shí)特別有用:
事件處理程序傳遞的對(duì)象有三個(gè)屬性:
lengthComputable —— 如果進(jìn)度可以計(jì)算,則設(shè)置為 true
total —— 消息體的工作總量或內(nèi)容長(zhǎng)度
loaded —— 到目前為止完成的工作或內(nèi)容的數(shù)量
Fetch API 沒有提供任何方法來監(jiān)控上傳進(jìn)度。
超時(shí)支持
XMLHttpRequest 對(duì)象提供了一個(gè) timeout 屬性,可以將其設(shè)置為請(qǐng)求自動(dòng)終止前允許運(yùn)行的毫秒數(shù);如果超時(shí),就觸發(fā)一個(gè) timeout 事件來處理:
fetch() 中可以封裝一個(gè)函數(shù)來實(shí)現(xiàn)超時(shí)功能:
或者,你可以使用 Promise.race():
這兩個(gè)方法都不容易使用,另外請(qǐng)求將在后臺(tái)繼續(xù)運(yùn)行。
中止支持
運(yùn)行中的請(qǐng)求可以通過 XMLHttpRequest 的 abort() 方法取消,如有必要,可以附加一個(gè) abort 事件來處理:
你可以中止一個(gè) fetch(),但它不是那么直接,需要一個(gè) AbortController 對(duì)象:
當(dāng) fetch() 中止時(shí),catch() 塊執(zhí)行。
更顯式的故障檢測(cè)
當(dāng)開發(fā)人員第一次使用 fetch() 時(shí),假設(shè)一個(gè) HTTP 錯(cuò)誤,如 404 Not Found 或 500 Internal Server error 將觸發(fā) Promise 拒絕并運(yùn)行相關(guān)的 catch() 塊,這似乎是合乎邏輯的,但事實(shí)并非如此:Promise 成功地解決了這些響應(yīng),只有當(dāng)網(wǎng)絡(luò)沒有響應(yīng)或請(qǐng)求被中斷時(shí),才會(huì)發(fā)生拒絕。
fetch() 的 Response 對(duì)象提供了 status 和 ok 屬性,但并不總是顯式地需要檢查它們,XMLHttpRequest 更明確,因?yàn)閱蝹€(gè)回調(diào)函數(shù)處理每一個(gè)結(jié)果:你應(yīng)該在每個(gè)示例中都看到 stuatus 檢查。
瀏覽器支持
我希望你不必支持 Internet Explorer 或 2015 年之前的瀏覽器版本,但如果是這樣的話,XMLHttpRequest 是你唯一的選擇。XMLHttpRequest 也很穩(wěn)定的,API 不太可能更新。Fetch 比較新,還缺少幾個(gè)關(guān)鍵特性,雖然更新不太可能破壞代碼,但你可以期待一些維護(hù)。
應(yīng)該使用哪個(gè) API ?
大多數(shù)開發(fā)人員都會(huì)使用更新的 Fetch API,它的語(yǔ)法更簡(jiǎn)潔,比 XMLHttpRequest 更有優(yōu)勢(shì);也就是說,這些好處中的許多都有特定的用例,但在大多數(shù)應(yīng)用程序中都不需要它們。只有兩種情況下 XMLHttpRequest 仍必不可少:
你正在支持非常老的瀏覽器——這種需求會(huì)隨著時(shí)間的推移而下降。
你需要顯示上傳進(jìn)度條。Fetch 后續(xù)將會(huì)支持,但可能需要幾年的時(shí)間。
這兩種選擇都很有趣,值得詳細(xì)了解它們!
轉(zhuǎn)載請(qǐng)?jiān)谖恼麻_頭和結(jié)尾顯眼處標(biāo)注:作者、出處和鏈接。不按規(guī)范轉(zhuǎn)載侵權(quán)必究。
未經(jīng)授權(quán)嚴(yán)禁轉(zhuǎn)載,授權(quán)事宜請(qǐng)聯(lián)系作者本人,侵權(quán)必究。
本文禁止轉(zhuǎn)載,侵權(quán)必究。
授權(quán)事宜請(qǐng)至數(shù)英微信公眾號(hào)(ID: digitaling) 后臺(tái)授權(quán),侵權(quán)必究。
評(píng)論
評(píng)論
推薦評(píng)論
暫無評(píng)論哦,快來評(píng)論一下吧!
全部評(píng)論(0條)