前端在页面关闭的时候使用 navigator.sendBeacon 发送请求。Beacon 请求发出的是异步请求,但是请求是作为浏览器任务执行的,与当前页面是脱钩的。因此该方法不会阻塞页面卸载流程和延迟后面页面的加载。
通俗的说,Beacon请求是页面的一种遗言机制,不需要响应,这时Beacon请求就会在部分浏览器的network页面中显示为pending,但是不会影响其他页面的加载,也不会占用浏览器资源。
不同浏览器中相同请求的状态显示
下图为在chrome中的请求状态
下图为在Firefox中的请求状态
可以看到在Firefox中可以识别到Beacon请求,当前状态也变为正常。
附:我们为什么要用navigator.sendBeacon
页面关闭的时候我们希望发送closesession请求,用来关闭当前的报表会话(注意不是 http 会话)。我们的要求是数据提交可靠,且不会影响下一页的加载体验。
1. 直接发送 xhr 请求
我们会先想到监听页面的unload
或者beforeunload
事件,在事件回调里使用XMLHttpRequest
发送异步请求。但是由于是xhr
请求是异步发送,很可能在它即将发送的时候,页面已经卸载了,从而导致发送取消或者发送失败。异步请求响应返回后,由于页面和相关资源已经卸载,也会引起错误。解决方法就是 AJAX 通信改成同步发送,即只有发送完成,页面才能卸载。这样可以解决发送数据的问题,但是存在两个问题:
- 部分浏览器已经不支持同步的 XMLHttpRequest 对象;
xhr
请求改为同步后,会阻塞页面的卸载和跳转,导致下一个页面导航加载的时机变晚,用户体验较差。
2. navigator.sendBeacon
Beacon请求发出的同样是异步请求,但是请求将会加入到浏览器队列中,作为浏览器任务执行的,与当前页面是脱钩的。因此该方法不会阻塞页面卸载流程和延迟后面页面的加载。使用 sendBeacon()
方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。这就解决了提交分析数据时的所有的问题:数据可靠,传输异步并且不会影响下一页面的加载。
sendBeacon 如果成功进入浏览器的发送队列后,会返回true;如果受到队列总数、数据大小的限制后,会返回false。返回ture后,只是表示进入了发送队列,浏览器会尽力保证发送成功,但是否成功了,无法判断。
浏览器兼容性
可以看到大部分浏览器已经支持 这个特性,因此可以作为主要的提交方式,由于IE系列并不支持这个特性,在无法获取到navigator.sendBeacon 的时候退回到同步ajax请求。
代码摘要:
提交关闭报表会话请求
$(window).unload(function () {
.......
if (navigator.sendBeacon) {
navigator.sendBeacon(FR.servletURL + "?op=closesessionid&sessionID=" + sid, new FormData());
} else {
var closeSession = function (sid) {
FR.ajax({
async: false,
url: FR.servletURL,
data: {
op: 'closesessionid',
sessionID: sid
}
});
};
closeSession(sid);
}
......
});