sendBeacon 在页面关闭前发送请求(onunload、onbeforeunload)

2,605 阅读2分钟

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

我们在做埋点的需求时,可能会遇到记录页面的停留时间,可能的实现方式是在进入页面发送一次埋点请求,在退出页面再发送一次。

但是我们会发现,在 unload 或者 beforeunload 时发送异步请求,会有请求被 cancel 的情况。那么如何在页面关闭前发送请求呢?

之前的做法

之前用过的可能的方法有这些:

  • 直接改成轮询请求,每n秒发送一次停留时间:这样做非常耗费资源,且统计出的时间也不够精确
  • 修改为同步 XMLHttpRequest 请求:这样是可以实现的,但是会使阻塞下一个页面的载入,让下一个页面载入变慢,不是一个好方式

之前的做法各有各的缺点,那么应该如何实现呢?

sendBeacon

navigator.sendBeacon() 这个方法正是为了解决这个问题提出的,它可以在页面关闭前使用,发送带有少量数据的 HTTP 异步请求,并且不会影响下一个页面的加载。

api 介绍

const result = navigator.sendBeacon(url, data);

它的第一个参数是接口地址;第二个参数是要发送的数据,可以是 ArrayBufferView, BlobDOMString 或者 FormData 格式

方法成功时会返回 true,否则返回 false

它的浏览器兼容性是没问题的~

最大数据大小

前面说到,sendBeacon 可以在页面关闭前使用,发送带有少量数据的 HTTP POST 异步请求,那么少量是指多少呢?stackoverflow 这个问题有写~

最大大小是由用户代理(浏览器或其他)决定的,www.w3.org/TR/beacon/#… ,你可以通过这种方式测试:

var url = 'http://jsfiddle.net?sendbeacon';
var n = 65536; // sendBeacon 在 Chrome v40 on Windows 上的大小限制(2^16)

// 来自 http://stackoverflow.com/questions/14343844/create-a-string-of-variable-length-filled-with-a-repeated-character
// 这里为了得到长度为 n+1,内容为 'X' 的字符串,现在也可以直接用 'X'.repeat(n+1)
var data = new Array(n+1).join('X'); 

if(!navigator.sendBeacon(url, data))
{
   alert('data limit reached');
}

例子

window.addEventListener("unload", sendData, false);

function sendData() {
  navigator.sendBeacon("/log", someData);
}

参考