在頁面關(guān)閉時(shí),前端上傳監(jiān)控?cái)?shù)據(jù)的4個(gè)解決方案

概覽
本文以 “前端監(jiān)控上報(bào)數(shù)據(jù)” 的業(yè)務(wù)場景,重點(diǎn)解析在 頁面實(shí)例關(guān)閉 時(shí),如何將監(jiān)控?cái)?shù)據(jù)上傳到服務(wù)端的解決方案。
其中,涉及到4種方案,分別為:

同步XMLHttpRequest
img.src
navigator.sendBeacon
fetch keepalive
同步XMLHttpRequest
const data = JSON.stringify({
     time: performance.now()
   });

var xhr = new XMLHttpRequest();

// 第三個(gè)參數(shù)false,表示當(dāng)前請求是同步
xhr.open('post', 'http://api.wangxiaokai.vip/test', false);

xhr.setRequestHeader('content-type', 'application/json');

xhr.onreadystatechange = function() {
 // 發(fā)送成功后,頁面已銷毀,所以這里執(zhí)行不了
}

xhr.send(data);

為什么同步XMLHttpRequest可以在頁面關(guān)閉時(shí)上傳數(shù)據(jù)?
同步請求阻止代碼的執(zhí)行,這會(huì)導(dǎo)致屏幕上出現(xiàn)“凍結(jié)”和無響應(yīng)的用戶體驗(yàn)。

然而,在新版的chrome(版本號大于80)已經(jīng)不支持。
以下是官方的公告片段:

Chrome now disallows synchronous calls to XMLHTTPRequest() during page dismissal when the page is being navigated away from or is closed by the user. This applies to beforeunload, unload, pagehide, and visibilitychange.

詳細(xì)解釋可閱讀 Disallow Synchronous XMLHTTPRequest\(\) in Page Dismissal[2]

缺點(diǎn)
用戶體驗(yàn)差,會(huì)阻塞頁面切換
只有舊版的瀏覽器支持
無法讀取reponse的返回值
img.src
創(chuàng)建一個(gè)<img>元素,并設(shè)置src。大部分的瀏覽器,都會(huì)延遲卸載當(dāng)前頁面,優(yōu)先加載圖像。

var data = JSON.stringify({
   time: performance.now()
 });
 
const img = new Image();
img.src = `http://api.wangxiaokai.vip/test?${JSON.stringify(data)}`;

缺點(diǎn)
數(shù)據(jù)傳輸不可靠,有可能瀏覽器卸載當(dāng)前頁面,直接殺掉圖像請求
只能發(fā)起GET請求
數(shù)據(jù)大小有限制
navigator.sendBeacon
通過HTTP POST請求,將少量數(shù)據(jù)使用異步的方式,發(fā)送到服務(wù)端。

function reportEvent() {
 const url = 'http://api.wangxiaokai.vip/test';
 const data = JSON.stringify({
   time: performance.now()
 });
 
 navigator.sendBeacon(url, data);
}

document.addEventListener('visibilitychange', function() {
 if (document.visiblityState === 'hidden') {
   reportEvent();
 }
});






發(fā)送的時(shí)機(jī)
瀏覽器端自動(dòng)判斷合適的時(shí)機(jī)進(jìn)行發(fā)送

是否會(huì)產(chǎn)生阻塞或影響頁面性能?
不會(huì)產(chǎn)生阻塞,影響當(dāng)前頁面的卸載。
不影響下個(gè)新頁面的加載,不存在性能問題。
另外,數(shù)據(jù)傳輸可靠。

語法
navigator.sendBeacon(url);
navigator.sendBeacon(url, data);

參數(shù)解析
url:接收請求的網(wǎng)絡(luò)地址
data:請求中攜帶的數(shù)據(jù),數(shù)據(jù)格式可選:ArrayBuffer,ArrayBufferView,Blob,DomString,F(xiàn)ormData,URLSearchParams
返回值
當(dāng)瀏覽器將數(shù)據(jù)成功加入傳輸隊(duì)列時(shí),sendBeacon方法會(huì)返回true,否則返回false。

注意返回值的時(shí)機(jī):成功加入傳輸隊(duì)列,而不是服務(wù)端的處理成功后的返回。

缺點(diǎn)
只能發(fā)起POST請求
無法自定義請求頭參數(shù)
數(shù)據(jù)大小有限制 (Chrome限制大小為64kb)
只能在window事件visibilitychange和beforeunload中使用,其他事件中回調(diào),會(huì)丟失數(shù)據(jù)。
兼容性



fetch keepalive
MDN web docs的描述如下 :

The keepalive option can be used to allow the request to outlive the page. Fetch with the keepalive flag is a replacement for the `Navigator.sendBeacon()`[3] API.

標(biāo)記keepalive的fetch請求允許在頁面卸載后執(zhí)行。

const url = 'http://api.wangxiaokai.vip/test';
const data = JSON.stringify({
   time: performance.now()
 });
 
fetch(url, {
 method: 'POST',
 body: data,
 headers: {
  'Content-Type': 'application/json'
 },
 keepalive: true,
});

兼容性



參考
Disallow Synchronous XMLHTTPRequest\(\) in Page Dismissal[4]
fetch\(\)[5]
You May Not Know Beacon[6]

關(guān)于本文:

來源:我是leon

https://juejin.cn/post/7106365076197605413

作者:leon


歡迎關(guān)注微信公眾號 :前端Q