浅析解决页面刷新/关闭之前发送请求上传数据的方案解析、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事件visibilitychangebeforeunload中使用,其他事件中回调,会丢失数据

4、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() API.

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

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》

posted @ 2021-04-12 18:49  古兰精  阅读(691)  评论(0编辑  收藏  举报