如何在web頁面關閉或重新整理之前,傳送Ajax到服務端
最近在做實時視訊這塊的封裝,需要客戶端Ajax實時上報狀態給服務端,以確認使用者的當前的通話狀態。這個時候,涉及到當用戶離開客戶端時候(關閉瀏覽器),傳送離線狀態給伺服器。
一、監聽瀏覽器關閉事件
瀏覽器關閉事件會有兩個,unload 和 **beforunload,兩者是有區別的,從字面上來看,beforunload 的呼叫是在unload 之前。
1、beforeunload 在使用者即將離開頁面時觸發,它返回一個字串,瀏覽器會向用戶展示並詢問這個字串以確定是否離開。
2、unload 在使用者已經離開時觸發,我們在這個階段僅可以做一些沒有延遲的操作,由於種種限制,很少被使用。
3、需要注意的是,如果在兩個事件監聽中新增 alert、confirm、prompt會忽略
4、生效情況:
- 1·關閉瀏覽器視窗
- 2·通過位址列或收藏夾前往其他頁面的時候
- 3·點選返回,前進,重新整理,主頁其中一個的時候
- 4·點選 一個前往其他頁面的url連線的時候
- 5·呼叫以下任意一個事件的時候:click,document.write()方法(輸出內容),document.open()開啟一個新的空白文件,document.close()方法可關閉一個由open()方法開啟的輸出流,並顯示選定的資料。 ,window close (),form.submit.
- 6·當用window open開啟一個頁面,並把本頁的window的名字傳給要開啟的頁面的時候。
- 7·重新賦予location.href的值的時候。
- 8·通過input type=”submit”按鈕提交一個具有指定action的表單的時候。
- 9.可以用在以下元素:body, frameset, window
==上程式碼==
window.onunload = function(){ // 相關程式碼 } window.onbeforeunload = function(){ // 相關程式碼 } window.addEventListener('unload', ()=>{ // 相關程式碼 }); window.addEventListener('beforeunload', ()=>{ // 相關程式碼 }); 複製程式碼
有時候兩個監聽需要配合使用,當然,避免重複呼叫,要做一些處理。
二、傳送請求
有了上面兩個監聽為前提下,我們來關心一下怎麼能把請求傳送成功給服務端。如果只是在監聽中隨便寫個請求,說不定請求還沒發出去,已經被瀏覽器停止了,非同步的Ajax不一定能準確的到達服務端。下面有三個想法可以進行實現。
1、請求設定為同步(不建議)
大家第一反應可能是,把請求設定為同步請求,上程式碼:
var xml = new XMLHttpRequest(); xml.open('POST', url , false); // false表示不是非同步請求 xml.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xml.onreadystatechange = function() { if (xml.readyState == 4 && xml.status == 200) { var responeData = xml.responseText; var data = JSON.parse(responeData); } else { // 處理異常 } }; xml.send('account=1&password=123456'); 複製程式碼
這種方法可以實現,但是對使用者體驗會有比較大的影響,並且在http協議最新的提案中,有考慮剔除請求同步標準
2、使用navigator中的sendBeacon傳送非同步請求(建議)
相關使用方法
body = JSON.stringify(body); // 原來是 xhr.send(body)變為 navigator.sendBeacon(url,body); 複製程式碼
支援傳送的body可以是ArrayBufferView, Blob, DOMString, 或者 FormData 型別的資料。 根據MDN的介紹:
主要用於滿足 統計和診斷程式碼的需要,這些程式碼通常嘗試在解除安裝(unload)文件之前向web伺服器傳送資料。過早的傳送資料可能導致錯過收集資料的機會。然而, 對於開發者來說保證在文件解除安裝期間傳送資料一直是一個困難。因為使用者代理通常會忽略在解除安裝事件處理器中產生的非同步 XMLHttpRequest 。 複製程式碼
下面介紹一下兩種其他不同型別的傳送方式
- (1) body使用Blob物件,有個好處是,可以自定義的設定header
blob = new Blob([`accoutn=12332323`], {type : 'application/x-www-form-urlencoded'}); navigator.sendBeacon("/leave", blob); 複製程式碼
- (2) 使用FormData物件,但是這時content-type會被自動變成"multipart/form-data"
var fd = new FormData(); fd.append('account', 123); navigator.sendBeacon("/leave", fd); 複製程式碼