页面隐藏时调用了 sendBeacon,返回 true,报表里却没有事件。排查时如果直接把 true 解释成"服务端已经收到",后面的网络、接收和持久化检查就会被跳过。
离页埋点首先要区分四种证据:浏览器接受排队、接收端确认、目标存储提交、查询能看到。它们回答的是不同阶段的问题,不能用一个布尔值替代。
一、true 只证明浏览器接受了排队
MDN 的 sendBeacon 文档明确说明,返回 true 表示浏览器成功把数据加入传输队列;false 表示未能排队。这不是 HTTP 响应,更不是持久化回执。
因此建议在排错记录里使用准确名称,例如"浏览器排队成功",不要叫"上报成功"。如果排队失败,要保留失败状态;如果排队成功但没有接收证据,则仍不能把这条事件标记成已入库。
文档也说明 Beacon 适合少量数据的异步 POST,并存在排队数据总量限制。不要把文档里的队列限制当成"每条请求都可占满的独立额度",也不要把一个页面所有业务对象直接塞进去。
二、hidden 不是永远离开,也不保证收尾完成
visibilitychange 的 hidden 状态可能来自切换标签、最小化、导航或切换应用。它是有价值的生命周期信号,却不是"用户永久结束会话"的业务事实。
用户可能切走十秒又回来。若每次 hidden 都生成一条"最终退出",很容易把同一次浏览过程切成多次退出,再把这些次数误解释成流失。
比较稳妥的合同是:先定义要记录的是"页面转为隐藏",还是"业务会话结束"。前者可以由浏览器观察;后者通常还需要明确的超时、重入和身份规则,不应仅从 hidden 推导。
三、把投递证据按阶段保存
可以为一条合成事件建立这样的验收卡,不把它当成某个产品的既有字段:
- 事件产生:保留稳定事件标识、页面实例标识和业务发生时间。
- 浏览器排队:记录采用的通道和排队结果,不写入整包敏感载荷。
- 接收确认:按接收接口的实际合同检查成功响应,区分接受处理和持久化完成。
- 存储与查询:用同一标识检查最终记录,并写清查询过滤、观察截止时间及延迟边界。
对于需要其他请求属性或读取响应的情形,MDN 建议考虑 fetch 的 keepalive。Request.keepalive允许请求在发起页面卸载后继续,但"允许继续"仍不是"保证送达";即使读到了响应,其含义也取决于服务端合同。
如果确实需要确认后删除本地待发记录,应另外设计可确认的协议,而不是从 sendBeacon 的 true 推导确认。本文不提供未经浏览器验证的持久化补报实现。
四、用六种场景验收,不只手动关一次页面
下面是合成测试设计,不是生产丢失率或兼容性测试结果。
- 正常切到后台再回来:只观察到隐藏与恢复,不自动增加一次"永久退出"。
- 连续切换两次:每次生命周期变化可独立观察,同一业务事件的重试仍保持原标识。
- 导航后通过历史返回:检查缓存恢复是否重复注册监听、重复生成同一业务事件。
- 移动端切应用再被终止:允许最后阶段缺证据,不把没有回调写成成功。
- 排队返回 false:验收记录保留失败,不与"已排队但尚未确认"混为一类。
- 接收端暂不可用:以最终接收和存储证据核对,不靠本地 true 把样本判为通过。
pagehide 与往返缓存兼容,但移动端某些终止路径不一定触发。beforeunload也不可靠,主要适用于未保存修改提示;不能为了埋点收尾而假设它必然执行。
验收离页埋点的关键不是找到一个"永不丢"的浏览器回调,而是让每个结论都有对应证据。缺少接收确认时写"未确认",缺少最终记录时写"尚未观察到",比笼统写"成功"更方便定位问题。
本文由 SensorFlow 项目维护方编写,使用 AI 辅助整理并核对官方资料。SensorFlow 接入文档可作为自托管接收链路的核对入口;合成 Demo 免许可证,真实 SDK 载荷接收需要商业许可证。本文的验收卡是方法示例,不表示产品内置了离页补报或确认协议。