久久国产精品一区二区三区四区,久色婷婷小香蕉久久,国产日韩欧美在线播放不卡,另类av一区二区

世界實時:如何在用戶離開頁面時可靠地發送 HTTP 請求
來源:前端全棧開發者    時間:2022-07-04 05:52:40

有幾次,當用戶執行導航到不同頁面或提交表單等操作時,我需要發送帶有一些數據的 HTTP 請求以進行記錄??紤]這個在點擊鏈接時向外部服務發送一些信息的人為示例:

Go to Page<script>document.getElementById("link").addEventListener("click", (e) => { fetch("/log", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ some: "data" }) });});</script>

這里沒有什么非常復雜的事情發生。該鏈接可以正常運行(我沒有使e.preventDefault()),但在該行為發生之前,會在單擊時觸發 POST 請求。無需等待任何形式的響應。我只是希望它被發送到我正在訪問的任何服務。

乍一看,您可能希望該請求的分派是同步的,之后我們將繼續導航離開頁面,而其他服務器成功地處理該請求。但事實證明,情況并非總是如此。


【資料圖】

瀏覽器不保證保留打開的HTTP請求

當瀏覽器中發生終止頁面的情況時,并不能保證進程內的HTTP請求會成功(參見更多關于“終止”和頁面生命周期的其他狀態)。這些請求的可靠性可能取決于幾個方面——網絡連接、應用程序性能,甚至外部服務本身的配置。因此,在這些時刻發送數據可能是不可靠的,如果您依賴這些日志來做出數據敏感的業務決策,那么這可能會帶來一個潛在的重大問題。為了幫助說明這種不可靠性,我使用上面包含的代碼設置了一個帶有頁面的小型 Express 應用程序。單擊鏈接時,瀏覽器會導航到 /other,但在此之前,會觸發 POST 請求。當一切都發生時,我打開了瀏覽器的網絡選項卡,并且我使用的是“慢 3G”連接速度。一旦頁面加載并且我已經清除了日志,事情看起來很安靜:

1.webp

但是一旦鏈接被點擊,事情就會出錯,當導航發生時,請求被取消。

2.webp

這使得我們對外部服務是否能夠處理請求缺乏信心。為了驗證這種行為,當我們使用window.location以編程方式導航時也會發生這種情況:

document.getElementById("link").addEventListener("click", (e) => {+ e.preventDefault(); // Request is queued, but cancelled as soon as navigation occurs. fetch("/log", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ some: "data" }), });+ window.location = e.target.href;});

無論導航以何種方式或何時發生,以及活動頁面何時終止,那些未完成的請求都有被放棄的風險。

但是為什么被取消了呢?

問題的根源在于,默認情況下,XHR 請求(通過 fetch 或 XMLHttpRequest)是異步且非阻塞的。一旦請求被排隊,請求的實際工作就會被移交給幕后的瀏覽器級 API。由于它與性能有關,這很好——你不希望請求占用主線程。但這也意味著當頁面進入“終止”狀態時,它們有被遺棄的風險,無法保證任何幕后工作都能完成。以下是 Google 對特定生命周期狀態的總結:

一旦頁面開始被瀏覽器卸載并從內存中清除,頁面就處于終止狀態。在這種狀態下沒有新的任務可以啟動,并且正在進行的任務如果運行時間過長可能會被殺死。

簡而言之,瀏覽器的設計假設當一個頁面被關閉時,沒有必要繼續處理它排隊的任何后臺進程。

那么,我們有哪些選擇呢?

避免這個問題最明顯的方法可能是,盡可能地延遲用戶操作,直到請求返回響應。在過去,通過使用XMLHttpRequest中支持的同步標志來實現這一點是錯誤的。使用它會完全阻塞主線程,導致大量的性能問題——我在過去寫過一些這方面的文章——所以這個想法甚至不應該被接受。事實上,它正在退出平臺(Chrome v80+已經刪除了它)。相反,如果您打算采用這種類型的方法,那么最好在響應返回時等待Promise解析。在它回來之后,您可以安全地執行該行為。使用我們之前的代碼片段,它可能看起來像這樣:

document.getElementById("link").addEventListener("click", async (e) => { e.preventDefault(); // Wait for response to come back... await fetch("/log", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ some: "data" }), }); // ...and THEN navigate away. window.location = e.target.href;});

這可以完成工作,但也有一些不小的缺點。首先,它會延遲所需行為的發生,從而損害用戶體驗。收集分析數據肯定會給業務(以及未來的用戶)帶來好處,但讓當前用戶為實現這些好處而支付成本并不理想。更不用說,作為一個外部依賴項,服務本身的任何延遲或其他性能問題都會暴露給用戶。如果分析服務的暫停導致客戶無法完成一項高價值的行動,那么所有人都是輸家。其次,這種方法并不像它最初聽起來那么可靠,因為一些終止行為無法通過編程延遲。例如, e.preventDefault() 無法延遲某人關閉瀏覽器選項卡。因此,它充其量只能涵蓋為某些用戶操作收集數據,但不足以全面信任它。

指示瀏覽器保留未完成的請求

值得慶幸的是,有一些選項可以保留絕大多數瀏覽器中內置的未完成的 HTTP 請求,并且不需要損害用戶體驗。

使用Fetch的keepalive標志

如果在使用fetch()時將keepalive標志設置為true,那么相應的請求將保持打開狀態,即使發起該請求的頁面被終止。使用我們最初的例子,它的實現如下所示:

Go to Page<script> document.getElementById("link").addEventListener("click", (e) => { fetch("/log", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ some: "data" }), keepalive: true }); });</script>

當點擊該鏈接并進行頁面導航時,不會發生請求取消:

3.webp

相反,我們得到的是一個(未知)狀態,原因很簡單,活動頁面從來沒有等待接收任何響應。像這樣的一行程序很容易修復,特別是當它是常用瀏覽器API的一部分時。但是,如果您正在尋找一種功能更集中、界面更簡單的選擇,那么還有另一種方法,它實際上具有相同的瀏覽器支持。

使用 Navigator.sendBeacon()

Navigator.sendBeacon() 函數專門用于發送單向請求(beacon)。一個基本的實現看起來像這樣,發送一個帶有字符串化 JSON 和“text/plain” Content-Type 的 POST:

navigator.sendBeacon("/log", JSON.stringify({ some: "data"}));

但是此 API 不允許您發送自定義標頭。因此,為了讓我們以“application/json”的形式發送數據,我們需要做一些小調整并使用 Blob:

Go to Page<script> document.getElementById("link").addEventListener("click", (e) => { const blob = new Blob([JSON.stringify({ some: "data" })], { type: "application/json; charset=UTF-8" }); navigator.sendBeacon("/log", blob)); });</script>

最后,我們得到了相同的結果——即使在頁面導航之后也允許完成的請求。但是還有一些事情可能使它比 fetch() 更有優勢:beacon以低優先級發送。為了演示,當同時使用帶有 keepalive 的 fetch() 和 sendBeacon() 時,Network 選項卡中顯示的內容如下:

4.webp

默認情況下,fetch() 獲得“高”優先級,而beacon(上面稱為“ping”類型)具有“最低”優先級。對于對頁面功能不重要的請求,這是一件好事。直接取自 Beacon 規范:

該規范定義了一個接口,[…]最大限度地減少與其他時間關鍵操作的資源爭用,同時確保此類請求仍然被處理并交付到目的地。

換句話說,sendBeacon() 確保它的請求不會妨礙那些對您的應用程序和用戶體驗真正重要的請求。

因為ping屬性而被光榮提及

值得一提的是,越來越多的瀏覽器支持 ping 屬性。當附加到鏈接時,它會觸發一個小的 POST 請求:

Go to Other Page

這些請求標頭將包含單擊鏈接的頁面(ping-from),以及該鏈接的 href 值(ping-to):

headers: { "ping-from": "http://localhost:3000/", "ping-to": "http://localhost:3000/other" "content-type": "text/ping" // ...other headers},

它在技術上類似于發送beacon,但有一些明顯的限制:

它嚴格限制在鏈接上的使用,如果您需要跟蹤與其他交互相關的數據,例如按鈕點擊或表單提交,這將使其無法啟動。

瀏覽器支持很好,但不是很好。在撰寫本文時,Firefox 特別沒有默認啟用它。

您無法隨請求一起發送任何自定義數據。正如前面提到的,您最多只能得到幾個ping-*頭文件,以及隨程序一起出現的任何其他頭文件。

綜合考慮,如果您可以發送簡單的請求并且不想編寫任何自定義 JavaScript,那么 ping 是一個很好的工具。但是,如果您需要發送更多實質內容,則可能不是最好的選擇。

那么,我應該選擇哪一個呢?

使用 fetch 和 keepalive 或 sendBeacon() 發送您的最后一秒請求肯定存在權衡。為了幫助辨別哪種方法最適合不同的情況,需要考慮以下幾點:

如果出現以下情況,您可能會使用 fetch() + keepalive:

您需要輕松地隨請求傳遞自定義標頭。您想向服務發出 GET 請求,而不是 POST。您正在支持較舊的瀏覽器(如 IE)并且已經加載了 fetch polyfill。

但在以下情況下 sendBeacon() 可能是更好的選擇:

您正在進行簡單的服務請求,不需要進行太多定制。您更喜歡更簡潔、更優雅的 API。您希望確保您的請求不會與應用程序中發送的其他高優先級請求競爭。

原文:https://css-tricks.com/send-an-http-request-on-page-exit/作者:Alex MacArthur

關鍵詞: 應用程序 用戶體驗 為了幫助 看起來像 活動頁面

上一篇:

下一篇:

X 關閉

X 關閉

久久国产精品一区二区三区四区,久色婷婷小香蕉久久,国产日韩欧美在线播放不卡,另类av一区二区
亚洲欧美日韩专区| 国产午夜精品麻豆| 国产精品视频网址| 国产精品红桃| 裸体女人亚洲精品一区| 免费观看30秒视频久久| 中文在线资源观看视频网站免费不卡| 国产精品久久久久久久久动漫| 亚洲一区二区三区视频播放| 欧美大学生性色视频| 国内精品视频在线观看| 亚洲黄色大片| 亚洲欧美日韩一区在线观看| 在线观看中文字幕亚洲| 亚洲一二三区在线| 欧美日韩专区在线| 在线播放精品| 99re国产精品| 国产精品国产a| 亚洲一二三四久久| 久久精品成人欧美大片古装| 亚洲自啪免费| 久久久久国色av免费看影院| 一区二区欧美在线观看| 欧美成人有码| 欧美一区二区在线观看| 国产乱理伦片在线观看夜一区| 久久一区激情| 国产视频综合在线| 国产午夜精品麻豆| 99精品视频一区二区三区| 午夜久久久久| 麻豆视频一区二区| 国产精品麻豆欧美日韩ww| 欧美一级片在线播放| 在线观看一区视频| 久久激情综合| 国产精品theporn| 一区二区三区 在线观看视| 国产精品影院在线观看| 男男成人高潮片免费网站| 一区二区三区产品免费精品久久75| 亚洲精品国产精品久久清纯直播| 99热在这里有精品免费| 欧美xxx在线观看| 国产精品高潮呻吟久久av无限| 蜜臀av一级做a爰片久久| 国产精品mm| 国产精品一区免费观看| 亚洲免费成人| 亚洲自拍都市欧美小说| 国产欧美日韩另类一区| 国产精品欧美一区喷水| 好看的日韩av电影| 国产精品久久久久国产a级| 欧美不卡高清| 欧美精品在线免费播放| 国产乱子伦一区二区三区国色天香| 国产一区二区中文字幕免费看| 免费成人高清视频| 欧美日韩精品| 99视频精品免费观看| 日韩视频不卡中文| 亚洲美女性视频| 亚洲老司机av| 日韩网站免费观看| 亚洲视频图片小说| 欧美精品www| 午夜欧美精品久久久久久久| 国产精品久久一级| 日韩视频亚洲视频| 91久久嫩草影院一区二区| 在线成人h网| 国产日韩欧美在线看| 国产精品久久精品日日| 国产香蕉97碰碰久久人人| 久久久最新网址| 激情久久综艺| 国产午夜精品麻豆| 影音先锋在线一区| 欧美全黄视频| 欧美精品性视频| 国产农村妇女精品| 亚洲视频在线一区观看| 久久久久久**毛片大全| 亚洲精品乱码视频| 国产欧美一区二区精品秋霞影院| 欧美成人一区在线| 亚洲午夜免费福利视频| 亚洲一区二区三区免费在线观看| 国产一级精品aaaaa看| 国产区精品视频| 亚洲伊人久久综合| 亚洲国产高清一区| 亚洲大片精品永久免费| 亚洲黄色有码视频| 国产精品爽爽ⅴa在线观看| 国产一区二区三区电影在线观看| 一区二区三区四区五区精品视频| 久久综合精品国产一区二区三区| 亚洲精品午夜精品| 欧美一区二区高清在线观看| 欧美在线一二三区| 欧美激情一区在线| 欧美顶级艳妇交换群宴| 在线成人av| 欧美日韩18| 国产精品久久久久久久久久妞妞| 欧美午夜视频网站| 亚洲一区在线直播| 欧美系列亚洲系列| 噜噜噜噜噜久久久久久91| 国产精品chinese| 亚洲精品欧美激情| 午夜精品一区二区在线观看| 国产精品三级视频| 国产精品女主播一区二区三区| 久久影视三级福利片| 久久这里只有| 先锋亚洲精品| 免费一级欧美片在线播放| 久久亚洲午夜电影| 欧美一区二区三区精品| 亚洲免费成人av| 国产欧美日韩综合| 欧美国产精品一区| 国产喷白浆一区二区三区| 国产精品高潮呻吟久久av无限| 亚洲一区在线免费| 欲色影视综合吧| 国产亚洲激情在线| 国产亚洲福利社区一区| 欧美日韩国产综合视频在线观看| 亚洲天堂免费在线观看视频| 久久久久久综合网天天| 亚洲一区在线直播| 午夜伦欧美伦电影理论片| 卡一卡二国产精品| 欧美在线观看网址综合| 欧美精品尤物在线| 国产精品久久综合| 久久精品国产免费看久久精品| 久久免费少妇高潮久久精品99| 激情久久中文字幕| 亚洲在线视频观看| 国产一区在线观看视频| 欧美视频一区二| 国产精品久久久久天堂| 亚洲第一天堂无码专区| 久久久噜噜噜久久中文字幕色伊伊| 欧美成人免费网站| 欧美日韩国产综合一区二区| 亚洲午夜精品一区二区三区他趣| 久久久综合免费视频| 国产亚洲精品高潮| 一区二区三区**美女毛片| 久久精品视频99| 亚洲高清视频在线观看| 欧美国产日韩视频| 亚洲天堂免费观看| 亚洲卡通欧美制服中文| 国产精品视频1区| 亚洲欧美视频在线| 久久久久久网址| 国产欧美一区二区三区沐欲| 亚洲天堂久久| 欧美大秀在线观看| 黄色成人小视频| 国产精品手机在线| 国产精品永久免费视频| 麻豆9191精品国产| 亚洲一二三四久久| 国产日韩欧美在线播放| 久久激情视频免费观看| 久久久久久**毛片大全| 悠悠资源网久久精品| 一本一道久久综合狠狠老精东影业| 国产精品美女999| 亚洲自拍偷拍麻豆| 韩国av一区二区三区在线观看| 亚洲欧美成人一区二区在线电影| 老色鬼久久亚洲一区二区| 欧美不卡视频一区| 国产欧美婷婷中文| 欧美国产日韩精品免费观看| 一本久久综合亚洲鲁鲁五月天| 免费观看久久久4p| 亚洲老板91色精品久久| 亚洲激情第一页| 激情综合在线| 欧美精品在线视频观看| 欧美日韩国产一区二区三区| 欧美人成免费网站| 小处雏高清一区二区三区| 国产精品你懂的| 欧美成年人网站| 亚洲精品久久久久久久久久久久久| 亚洲精品在线一区二区| 国产欧美日韩亚洲一区二区三区| 国产精品夫妻自拍|