概览
本文以 “前端监控上报数据” 的业务场景,重点解析在 页面实例关闭 时,如何将监控数据上传到服务端的解决方案。
其中,涉及到4种方案,分别为:
- 同步XMLHttpRequest
- img.src
- navigator.sendBeacon
- fetch keepalive
同步XMLHttpRequest
const data = JSON.stringify({
time: performance.now()
});
var xhr = new XMLHttpRequest();
// 第三个参数false,表示当前请求是同步
xhr.open('post', 'http://api.wangxiaokai.vip/test', false);
xhr.setRequestHeader('content-type', 'application/json');
xhr.onreadystatechange = function() {
// 发送成功后,页面已销毁,所以这里执行不了
}
xhr.send(data);
复制代码
为什么同步XMLHttpRequest可以在页面关闭时上传数据?
同步请求阻止代码的执行,这会导致屏幕上出现“冻结”和无响应的用户体验。
然而,在新版的chrome(版本号大于80)已经不支持。
以下是官方的公告片段:
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 tobeforeunload
,unload
,pagehide
, andvisibilitychange
.
详细解释可阅读 Disallow Synchronous XMLHTTPRequest\(\) in Page Dismissal[2]
缺点
- 用户体验差,会阻塞页面切换
- 只有旧版的浏览器支持
- 无法读取
reponse
的返回值
img.src
创建一个<img>
元素,并设置src
。大部分的浏览器,都会延迟卸载当前页面,优先加载图像。
var data = JSON.stringify({
time: performance.now()
});
const img = new Image();
img.src = `http://api.wangxiaokai.vip/test?${JSON.stringify(data)}`;
复制代码
缺点
- 数据传输不可靠,有可能浏览器卸载当前页面,直接杀掉图像请求
- 只能发起
GET
请求 - 数据大小有限制
navigator.sendBeacon
通过HTTP POST
请求,将少量数据使用异步
的方式,发送到服务端。
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();
}
});
复制代码
发送的时机
浏览器端自动判断合适的时机进行发送
是否会产生阻塞或影响页面性能?
不会产生阻塞,影响当前页面的卸载。
不影响下个新页面的加载,不存在性能问题。
另外,数据传输可靠。
语法
navigator.sendBeacon(url);
navigator.sendBeacon(url, data);
复制代码
参数解析
url
:接收请求的网络地址data
:请求中携带的数据,数据格式可选:ArrayBuffer
,ArrayBufferView
,Blob
,DomString
,FormData
,URLSearchParams
返回值
当浏览器将数据成功加入传输队列时,sendBeacon
方法会返回true
,否则返回false
。
注意返回值的时机:成功加入传输队列,而不是服务端的处理成功后的返回。
缺点
- 只能发起
POST
请求 - 无法自定义请求头参数
- 数据大小有限制 (Chrome限制大小为
64kb
) - 只能在window事件
visibilitychange
和beforeunload
中使用,其他事件中回调,会丢失数据。
兼容性
fetch keepalive
MDN web docs的描述如下 :
The
keepalive
option can be used to allow the request to outlive the page. Fetch with thekeepalive
flag is a replacement for the `Navigator.sendBeacon()`[3] API.
标记keepalive
的fetch
请求允许在页面卸载后执行。
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,
});
复制代码
兼容性