在页面关闭时,前端上传监控数据的4个解决方案

335次阅读  |  发布于2年以前

概览

本文以 “前端监控上报数据” 的业务场景,重点解析在 页面实例关闭 时,如何将监控数据上传到服务端的解决方案。 其中,涉及到4种方案,分别为:

同步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 to beforeunload, unload, pagehide, and visibilitychange.

详细解释可阅读 Disallow Synchronous XMLHTTPRequest\(\) in Page Dismissal[2]

缺点

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)}`;
复制代码

缺点

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);
复制代码

参数解析

返回值

当浏览器将数据成功加入传输队列时,sendBeacon方法会返回true,否则返回false

注意返回值的时机:成功加入传输队列,而不是服务端的处理成功后的返回。

缺点

兼容性

image.png

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.

标记keepalivefetch请求允许在页面卸载后执行。

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,
});
复制代码

兼容性

image.png

参考

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8