跳到主要内容

sendBeacon

2024年04月24日
柏拉文
越努力,越幸运

一、认识


navigator.sendBeacon() 方法可用于通过 HTTP POST 将少量数据异步传输到 Web 服务器。它主要用于将统计数据发送到 Web 服务器,同时避免了用传统技术(如:XMLHttpRequest)发送分析数据的一些问题。

navigator.sendBeacon 通过 HTTP POST 发送的请求不会在浏览器中的 Network 出现,但是服务端确实可以接收到的。

二、对比


2.1 sendBeacon vs IMG

我们可以以向服务端请求图片资源的形式,像服务端传输少量数据,这种方式不会造成跨域。但是 Image 是以 GET 方式请求图片资源的方式,将上报数据附在 URL 上携带到服务端,而 URL 地址的长度是有一定限制的。规范对 URL 长度并没有要求,但是浏览器、服务器、代理服务器都对 URL 长度有要求。有的浏览器要求URLpath部分不超过 2048,这就导致有些请求会发送不完全。

navigator.sendBeacon 是以 HTTP POST 的方式异步上传数据, 相比 HTTP GET 可以传输更多的数据。

2.1 sendBeacon vs XMLHttpRequest & IMG

过去,为了上报埋点数据, 通常是:

  • IMG

  • XMLHttpRequest

上述的所有方法都会迫使用户代理延迟卸载文档,并使得下一个导航出现的更晚。下一个页面对于这种较差的载入表现无能为力。

这就是 sendBeacon() 方法存在的意义。使用 sendBeacon() 方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。这意味着:

  • 数据发送是可靠的

  • 数据异步传输

  • 不影响下一导航的载入

2.43 sendBeacon report schema summarize

navigator.sendBeacon 会在合适的时机通过 HTTP POST 将少量数据异步发送到服务端, 不会阻塞当前页面的卸载, 也不会阻塞下个新页面的加载, 不存在性能问题, 这意味着 navigator.sendBeacon 在可靠、异步发送数据的同时, 不会影响当前页面、下一个页面。

三、语法


3.1 点击事件上报

function buryingPointBySendBeacon(type) {
const params = JSON.stringify({
type,
url: "sendBeacon",
device: "pc",
});
const result = navigator.sendBeacon(
"/server/buryingPoint/sendBeacon",
params
);
console.log(result);
}

const buttonSendBeacon = document.querySelector("#button-sendBeacon");
buttonSendBeacon.addEventListener("click", function () {
buryingPointBySendBeacon("sendBeacon");
});

3.2 会话结束上报

网站通常希望在用户完成页面浏览后向服务器发送分析或诊断数据,最可靠的方法是在 visibilitychange 事件发生时发送数据。我们应该避免使用 unloadbeforeunload 事件以在会话结束时发送统计数据。然而这是不可靠的,在许多情况下(尤其是移动设备)浏览器不会产生 unloadbeforeunloadpagehide 事件。下面列出了一种不触发上述事件的情况:

  1. 用户加载了网页并与其交互

  2. 完成浏览后,用户切换到了其他应用程序,而不是关闭选项卡

  3. 随后,用户通过手机的应用管理器关闭了浏览器应用

此外,unload 事件与现代浏览器实现的往返缓存(bfcache)不兼容。在部分浏览器(如:Firefox)通过在 bfcache 中排除包含 unload 事件处理器的页面来解决不兼容问题,但这存在性能损失。其他浏览器,例如 SafariAndroid 上的 Chrome 浏览器则采取用户在同一标签页下导航至其他页面时不触发 unload 事件的方法来解决不兼容问题。Firefox 也会在 bfcache 中排除包含 beforeunload 事件处理器的页面。

综上所述: 我们如果在会话结束后上报数据,应该使用 visibilitychange 事件发送数据。如果 visibilitychange 在某个浏览器未实现, 可以优雅降级为 pagehide , 和 beforeunloadunload 事件类似,pagehide 这一事件不会被可靠地触发(特别是在移动设备上),但它与 bfcache 兼容。

function buryingPointBySendBeacon(type) {
const params = JSON.stringify({
type,
url: "sendBeacon",
device: "pc",
});
const result = navigator.sendBeacon(
"/server/buryingPoint/sendBeacon",
params
);
console.log(result);
}

function handleVisibilitychange() {
if (document.visibilityState === "hidden") {
buryingPointBySendBeacon();
}
}

document.addEventListener("visibilitychange", handleVisibilitychange);

四、请求


navigator.sendBeacon 发送的请求是 ping 类型, 所以只会在 Network 中的 All 或者 Other 里出现, 不会出现在 Fetch/XHR 列表中。如图所示:

Preview