浅析解决页面刷新/关闭之前发送请求上传数据的方案解析、Navigator.sendBeacon()介绍及意义
一、在页面关闭时,前端上传监控数据的5个解决方案
1、同步XMLHttpRequest
const data = JSON.stringify({ time: performance.now() });
var xhr = new XMLHttpRequest();
// 第三个参数false,表示当前请求是同步
xhr.open('post', 'http://***/test', false);
xhr.setRequestHeader('content-type', 'application/json');
xhr.onreadystatechange = function() {
// 发送成功后,页面已销毁,所以这里执行不了
}
xhr.send(data);
缺点:(1)用户体验差,会阻塞页面切换(2)只有旧版的浏览器支持,新版本浏览器已不再支持(3)无法读取reponse
的返回值
2、img.src 创建一个<img>
元素,并设置src
。大部分的浏览器,都会延迟卸载当前页面,优先加载图像。
const img = new Image();
img.src = `http://***/test?${JSON.stringify(data)}`;
缺点:(1)数据传输不可靠,有可能浏览器卸载当前页面,直接杀掉图像请求(2)只能发起Get请求(3)数据大小有限制
3、navigator.sendBeacon
通过HTTP POST
请求,将少量数据使用异步
的方式,发送到服务端。
function reportEvent() {
const url = 'http://***/test';
const data = JSON.stringify({
time: performance.now()
});
navigator.sendBeacon(url, data);
}
document.addEventListener('visibilitychange', function() {
if (document.visiblityState === 'hidden') {
reportEvent();
}
});
(1)发送的时机
浏览器端自动判断合适的时机进行发送
(2)是否会产生阻塞或影响页面性能?
不会产生阻塞,不会影响当前页面的卸载。
不影响下个新页面的加载,不存在性能问题。
另外,数据传输可靠。
(3)语法:navigator.sendBeacon(url, data);
(4)返回值
当浏览器将数据成功加入传输队列时,sendBeacon
方法会返回true
,否则返回false
。
注意返回值的时机:成功加入传输队列,而不是服务端的处理成功后的返回。
(5)缺点
- 只能发起
POST
请求 - 无法自定义请求头参数
- 数据大小有限制 (Chrome限制大小为
64kb
) - 只能在window事件
visibilitychange
和beforeunload
中使用,其他事件中回调,会丢失数据
4、fetch keepalive
MDN web docs的描述如下:
Thekeepalive
option can be used to allow the request to outlive the page. Fetch with thekeepalive
flag is a replacement for theNavigator.sendBeacon()
API.
标记keepalive
的fetch
请求允许在页面卸载后执行。
const url = 'http://***/test';
const data = ***
fetch(url, {
method: 'POST',
body: data,
headers: {'Content-Type': 'application/json'},
keepalive: true,
});
4种方案来源于学习文章《在页面关闭时,前端上传监控数据的4个解决方案》https://juejin.cn/post/7106365076197605413
二、Navigator.sendBeacon()介绍及意义
在项目中遇见 「需要在页面关闭时发送请求以获得用户关闭时间,用来计算用户网页浏览时长」 的需求
首先是使用 window.$.ajax 方法正常发送请求,但是async:false的情况下接口报错 NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load xxxx,将async设置为true则时而无法请求成功,这是因为如果请求是异步,页面关闭速度快于接口返回速度,则请求无法完成
后来发现navigator.sendBeacon(url,data)方法,可完美解决此问题
1、问题描述:
这个方法主要用于满足统计和诊断代码的需要,这些代码通常尝试在卸载(unload)文档之前向web服务器发送数据。过早的发送数据可能导致错过收集数据的机会。然而,对于开发者来说保证在文档卸载期间发送数据一直是一个困难。因为用户代理通常会忽略在 unload (en-US)
事件处理器中产生的异步 XMLHttpRequest
。
为了解决这个问题, 统计和诊断代码通常要在 unload
或者 beforeunload (en-US)
事件处理器中发起一个同步 XMLHttpRequest
来发送数据。同步的 XMLHttpRequest
迫使用户代理延迟卸载文档,并使得下一个导航出现的更晚。下一个页面对于这种较差的载入表现无能为力。
有一些技术被用来保证数据的发送。其中一种是通过在卸载事件处理器中创建一个图片元素并设置它的 src 属性的方法来延迟卸载以保证数据的发送。因为绝大多数用户代理会延迟卸载以保证图片的载入,所以数据可以在卸载事件中发送。另一种技术是通过创建一个几秒钟的 no-op 循环来延迟卸载并向服务器发送数据。
这些技术不仅编码模式不好,其中的一些甚至并不可靠而且会导致非常差的页面载入性能。下面的例子展示了一个理论上的统计代码——在卸载事件处理器中尝试通过一个同步的 XMLHttpRequest
向服务器发送数据。这导致了页面卸载被延迟。
window.addEventListener('unload', logData, false);
function logData() {
var client = new XMLHttpRequest();
client.open("POST", "/log", false); // 第三个参数表明是同步的 xhr
client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
client.send(analyticsData);
}
这就是 sendBeacon()
方法存在的意义。使用 sendBeacon()
方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。这就解决了提交分析数据时的所有的问题:数据可靠,传输异步并且不会影响下一页面的加载。此外,代码实际上还要比其他技术简单许多!
下面的例子展示了一个理论上的统计代码模式——通过使用 sendBeacon()
方法向服务器发送数据
window.addEventListener('unload', logData, false);
function logData() {
navigator.sendBeacon("/log", analyticsData);
}
具体意义及问题处理描述,可见这篇文章《Web Beacon 刷新/关闭页面之前发送请求 - https://blog.csdn.net/yuqing1008/article/details/103523427》