数据上报为什么要用 sendBeacon?页面关闭时丢数据的解决方案

摘要:埋点最怕"该记的数据没记到"。用户点击下单、页面随即关闭,普通请求在卸载阶段被浏览器取消,数据就丢了。sendBeacon 正是为"页面关闭时也能可靠送达"设计的上报方案。本文讲清它的原理、用法、兼容降级,以及怎么验证数据没丢。

先看一个数据层面的背景:第三方脚本已经是网页的"标配"。HTTP Archive《2025 Web Almanac》统计,全球超过 90% 的网页至少加载一个第三方资源,访问量最高的 1000 个站点桌面端第三方请求中位数达 129 个。这些第三方脚本大部分干的是同一件事:上报数据。而所有上报里,最容易丢的一类就是"发生在页面即将关闭那一瞬间的事件"——比如下单成功、点击外链、看完最后一个页面。

结论:页面关闭时的数据丢失,根因是浏览器在卸载阶段会取消未完成的网络请求。sendBeacon 专门解决这个问题:它让浏览器在页面卸载时以"尽力送达"的方式异步发送数据,不阻塞页面、不依赖请求是否完成。用法一句话:navigator.sendBeacon(url, data),不支持时用 fetch keepalive 或本地暂存补发兜底。

一、数据是怎么丢的

页面关闭数据丢失对比

用户点击按钮触发上报时,如果你用 XHR 或普通 fetch:

  • 页面进入卸载流程(关闭标签页、刷新、跳转),浏览器会取消尚未完成的网络请求;
  • 请求发出去了,但响应还没回来(甚至请求还没真正发出),数据就没了;
  • 在 beforeunload/pagehide 里同步发 XHR 能部分缓解,但会阻塞页面、体验差,且移动端并不可靠。

二、sendBeacon 为什么能送达

sendBeacon 由浏览器底层处理:它把数据交给浏览器后立即返回,浏览器在合适时机(含页面卸载后)把请求发出,不参与页面生命周期阻塞。特性要点:

特性说明
异步不阻塞调用后立即返回,不阻塞页面关闭
卸载阶段可靠页面关闭/跳转时仍会发送
轻量载荷适合小体积数据(事件、统计),不适合大文件
跨域支持可发送到任意允许的端点,由浏览器处理凭证策略
// 页面关闭前上报,一行代码
const sent = navigator.sendBeacon(
  '/api/track',
  new Blob([JSON.stringify(event)], { type: 'application/json' })
);
if (!sent) {
  // 队列满了或浏览器不支持,走降级
  fallbackReport(event);
}

三、兼容与降级:不能"只信 sendBeacon"

信标上报降级流程

sendBeacon 在现代浏览器中支持广泛(MDN 记录其为基础广泛可用的接口),但仍有边界:队列容量有限、部分 WebView 行为不一致、旧环境不支持。标准降级链:

  1. 首选navigator.sendBeacon()
  2. 次选:fetch 带 keepalive: true(语义相近,同样适合卸载期);
  3. 兜底:把事件写入 localStorage/sessionStorage,下次进入页面时补发。
function report(event) {
  const body = JSON.stringify(event);
  if (navigator.sendBeacon) {
    navigator.sendBeacon('/api/track', body);
  } else if (navigator.keepalive && window.fetch) {
    fetch('/api/track', { method: 'POST', body, keepalive: true });
  } else {
    saveToLocalQueue(event); // 本地暂存,下次补发
  }
}

四、怎么验证数据没丢

上线后做三件事确认有效性:

  • 埋点对账:把页面关闭类事件(点击外链、关闭前停留时长)与"服务端日志里实际收到的条数"对比,计算丢失率;
  • 场景实测:分别测"普通跳转、刷新、关闭标签页、移动端切后台"四种场景的送达情况;
  • 总量观察:对比改造前后同一事件的接收量级,正常应显著上升(说明之前确实在丢)。

踩坑记录:现象——改用 sendBeacon 后,数据量反而"少了"。根因——服务端按"请求完成返回 200"统计接收,而 sendBeacon 是"浏览器尽力发送",服务端日志按响应计数会漏掉部分送达;同时页面在 beforeunload 里同步发 XHR 的老代码没删,事件被重复发送被去重掉了。排查证据——对比网关访问日志(按请求到达计)与应用日志(按响应计)数量不一致。修复方式——服务端改按"请求到达"计数、幂等去重,并清理重复上报代码。经验:sendBeacon 的"送达"以服务端收到为准,统计口径要从"响应"改成"到达",否则你会误判它在丢数据。

FAQ

Q1:sendBeacon 能传多大的数据?

A:浏览器有配额限制(一般 64KB 量级,各浏览器不同),适合事件类小载荷;大数据应走分片或队列。

Q2:和 fetch keepalive 有什么区别?

A:语义相近,都是卸载期可用的上报手段;sendBeacon 更专注"发送",keepalive 是 fetch 的一个选项,能拿到响应状态。现代环境二者皆可。

Q3:一定要在卸载事件里调用吗?

A:不必须。日常事件直接用 sendBeacon 也可以;只有"关闭/跳转瞬间"的事件必须借助它才能在卸载时送达。

Q4:WebView / 小程序里能用吗?

A:依赖内核支持;不支持的 WebView 走本地暂存补发,或使用宿主环境提供的专用上报通道。

Q5:上报会不会被浏览器丢弃?

A:存在"尽力而为"的不确定性(如系统资源极端紧张),所以关键事件建议加本地暂存兜底,做到"能补则补"。

总结

页面关闭时丢数据,是埋点链路里最隐蔽的缺口之一:业务在增长,但"最后一步"一直没记全。sendBeacon 用一行代码补上这个缺口——异步、不阻塞、卸载期可靠送达;再配合 keepalive 与本地暂存两级降级,以及"按到达计数"的验证口径,就能让关闭类事件真正"颗粒归仓"。

参考资料:MDN Web Docs:sendBeacon 接口文档与浏览器兼容性数据;
                  HTTP Archive《2025 Web Almanac(Third Parties 章节)》;
                  WHATWG HTML 规范中关于卸载期请求行为的相关说明(经社区转引)。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值