如何在web頁面關閉或重新整理之前,傳送Ajax到服務端

炎火師發表於2019-04-19

最近在做實時視訊這塊的封裝,需要客戶端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);
複製程式碼

3、服務端實現接收

=-=結束語:通過以上兩種結合,可以在頁面結束時候,進行Ajax請求的傳送=-=

原文參考相關連結:juejin.im/post/5c7e54…

相關文章