【兼容性】監聽頁面關閉發送請求

這個是前端兼容性系列內容


因爲前端監控會在頁面關閉的時候,發送一下日誌,所以會涉及到監聽頁面關閉,之前我們只監聽了一個beforeunload 來發送數據

但是我看了之後發現應該沒有這麼簡單實現,前端總要寫一些亂七八糟的兼容代碼的啊!

於是就去研究了一下,好傢伙,兼容性五花八門

我測試的終端包括

1、Windows PC  (10),Chrome

2、iOS(14,13,12,11),Safari

3、Android (10,9),自帶瀏覽器

4、HarmonyOS(1),自帶瀏覽器

是的,我還測試了鴻蒙,華爲 yyds!

下面就來詳細說說,本文分爲

1、頁面關閉動作

2、頁面關閉事件

3、測試結論

4、兼容做法

5、頁面關閉發送請求

親身多次實驗,but 數據僅供參考


頁面關閉動作

我仔細想了想所有會導致頁面關閉的動作

1、頁面刷新

2、跳轉頁面

3、關閉tab

4、關閉瀏覽器

所以如果我要監聽頁面關閉,那麼我必須要都兼容這些動作。

我是怎麼做這些動作的,關閉tab ,pc 的不用說了吧

移動端就是打開瀏覽器的窗口界面,然後關閉

關閉瀏覽器則是在任務管理界面,把 app 劃出


頁面關閉事件

頁面關閉有哪些事件,我直接列出來

1、beforeunload

2、pagehide

3、unload

它們觸發的順序和列出來的一樣,beforeunload->pagehide->unload

下面來看針對這些事件的兼容情況


測試結論

PC 端對於上面 四個動作,3個事件 都支持,移動端則表現不一

先綜述一下

1、 iOS 壓根就不支持 beforeunload,unload 根據 iOS 版本支持程度也較低

2、Android 只有刷新支持 beforeunload,而 unload 的話好一些,支持 刷新和關閉tab

3、HarmonyOs ,刷新和跳轉 支持 beforeunload,unload 只有 刷新支持

具體數據如下

所以綜上所述,beforeunload 和 unload 在移動端並不是十分可靠

而 iOS 開發文檔也說明了,load 這類事件支持不好,最好使用 pagehide 事件 

https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html#//apple_ref/doc/uid/TP40006511-SW5

經過測試,結論如下

pagehide 的確支持程度要好很多,不管是PC 還是移動端,但是終究沒能全部覆蓋,有點遺憾啊,難道要拋棄這部分了嗎

轉念想了想, visibilitychange會在頁面可見或隱藏時觸發,或許能解決掉一部分

測試了一下,如下

WC,完全對 iOS 不支持啊,但是可以看見的確解決了一部分問題,把上面 HarmonyOs 、Android 都支持了

所以現在就剩兩種情況無法監聽到頁面關閉了

1、關閉 tab 時,iOS14 以下(iOS13、iOS12、iOS11,其他版本未測)
2、關閉瀏覽器時,iOS 全不支持

這兩種情況也沒有什麼好的辦法,但是考慮到在移動端關閉應用通常是App切到後臺然後再上滑關閉

而 iOS 在把瀏覽器切後臺的時候,可以觸發 visibilitychange ,所以可以算是解決掉 關閉瀏覽器的問題

至於關閉 tab,我調查大概四五個人,很少有關閉tab 的習慣,所以也不算是什麼大問題


兼容做法

所以現在如果我們要監聽頁面關閉,那麼我們最好監聽四種事件,這樣可以最大程度兼容

使用一個變量去判定是否已經執行過 頁面關閉相關的處理邏輯

只要執行就行,誰執行沒有關係,大家排好隊

let isEndSendOK = false;
function report({
  if (isEndSendOK) {
    return;
  }
  isEndSendOK = true;
  fetch('xxxxxx');
}

// 監聽多個事件,做同一個事情,用一個標誌位確定是否做過
// 移動端普遍只支持 pagehide
window.addEventListener('beforeunload', report);
window.addEventListener('pagehide', report);
window.addEventListener('unload', report);

// IOS14 之前不會冒泡,只能監聽document
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState !== 'visible') {
    report();
  } else {
    // 如果界面又顯示了,說明沒有關閉,重置標誌位
    isEndSendOK = false;
  }
});




頁面關閉發送請求

在頁面關閉的時候發送請求,因爲請求是異步的,所以大多數時候並不一定成功

使用同步的方式發送請求是可以,但是 會迫使用戶代理延遲卸載文檔,並使得下一個導航出現的更晚。

會導致非常差的下一個頁面載入性能,所以如果你的頁面在體驗發現加載慢,有可能是上一個頁面的鍋

上一個使用 同步方式發送 XHR 的例子

function xhrSync(type{
  var request = new XMLHttpRequest();
  request.open(
    'POST',
    'https://ke.qq.com/report' ,
    false  //false表示同步請求
  );
  request.send('xhr');
}

並且現在有部分瀏覽器針對這個情況,已經不允許在頁面關閉的時候發送同步請求了,不然就會報錯

Chrome 在文檔中也有相關的說明

https://www.chromestatus.com/feature/4664843055398912

Chrome now disallows synchronous XHR during page dismissal when the page is being navigated away from or closed by the user. This involves the following events (when fired on the path of page dismissal): beforeunload, unload, pagehide, and visibilitychange

反正就是不給你發!不然我報錯!

反正異步發送不靠譜,同步又不好

異步到底有多步靠譜,我還做了個測試,看看異步和同步的支持情況,數據如下,僅供參考

表示能成功收到請求,表示收不到請求,Error 表示報錯


你可能會問,你關閉 tab 和 瀏覽器,你是怎麼抓到請求的,因爲我的頁面用 whistle 代理,請求會經過 whistle,所以可以在界面上看到所有抓到的請求,不會的可以參考 前端調試必備-whistle 入門


你可能會問,你關閉 tab 和 瀏覽器,你是怎麼抓到請求的

因爲我的頁面用 whistle 代理,請求會經過 whistle,所以可以在界面上看到所有抓到的請求

反正就是不行!

針對這個情況, navigator.sendBeacon() 方法就出現了

會使用戶代理在有機會時異步地向服務器發送數據,同時不會延遲頁面的卸載或影響下一導航的載入性能。這就解決了提交分析數據時的所有的問題:數據可靠,傳輸異步並且不會影響下一頁面的加載

yyds!

但是好像支持情況也不太好嘛,IE 再見了


經過實測,只有 iOS11 沒有sendBeacon 這個方法,其他的 HarmonyOS ,Android,PC、iOS11以上 都能成功發送請求

但是呢,誒,但是,哪裏有這麼完美的東西呢

sendBeacon 只支持發送少量數據,如果發送太大的數據,是會報錯的

具體是返回一個false,表示該請求無法加入傳輸隊列

我查的資料以及自己嘗試,最大是 64KB,多1B 都不行

例子我是拷貝的

function testBeaconLimit({
  var url = 'https://ke.qq.com/report';
  var n = 1024 * 64

  // this method courtesy of http://stackoverflow.com/questions/14343844/create-a-string-of-variable-length-filled-with-a-repeated-character
  var data = new Array(n + 1).join('X'); // generate string of length n

  if (!navigator.sendBeacon(url, data)) {
    alert('data limit reached');
  }
}

並且還有一個要注意的

雖然說是最大 64KB,但是頻繁發送64KB 也有可能會錯誤,所以使用 sendBeacon 一定要做好錯誤兼容哦

至於這裏發送請求的兼容做法的話

如果你不在乎性能,可以先使用 sendBeacon 發送,不支持或者報錯,再使用同步的 XMLHttpRequest

但是因爲同步的 xhr 可能會報錯,也要做好錯誤處理

我搜到一個開源庫的sendBeacon兼容處理

https://github.com/miguelmota/Navigator.sendBeacon/blob/master/sendbeacon.js

可以參考一下


最後

鑑於本人能力有限,難免會有疏漏錯誤的地方,請大家多多包涵, 如果有任何描述不當的地方,歡迎後臺聯繫本人,領取紅包

本文分享自微信公衆號 - 神仙朱(skying-zhu)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章