这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战
我们在做埋点的需求时,可能会遇到记录页面的停留时间,可能的实现方式是在进入页面发送一次埋点请求,在退出页面再发送一次。
但是我们会发现,在 unload 或者 beforeunload 时发送异步请求,会有请求被 cancel 的情况。那么如何在页面关闭前发送请求呢?
之前的做法
之前用过的可能的方法有这些:
- 直接改成轮询请求,每n秒发送一次停留时间:这样做非常耗费资源,且统计出的时间也不够精确
- 修改为同步
XMLHttpRequest请求:这样是可以实现的,但是会使阻塞下一个页面的载入,让下一个页面载入变慢,不是一个好方式
之前的做法各有各的缺点,那么应该如何实现呢?
sendBeacon
navigator.sendBeacon() 这个方法正是为了解决这个问题提出的,它可以在页面关闭前使用,发送带有少量数据的 HTTP 异步请求,并且不会影响下一个页面的加载。
api 介绍
const result = navigator.sendBeacon(url, data);
它的第一个参数是接口地址;第二个参数是要发送的数据,可以是 ArrayBufferView, Blob, DOMString 或者 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);
}