navigator.sendBeacon 是页面卸载前异步发送 DOM 状态埋点的唯一可靠机制,它不阻塞卸载、支持多种数据格式、无需 CORS 预检,需配合 beforeunload/pagehide 事件快照状态并控制 payload ≤64KB。

navigator.sendBeacon 是浏览器提供的唯一能在页面卸载(如关闭、刷新、导航)前异步发送数据且不阻塞卸载流程的机制。它不保证“成功送达服务器”,但能最大程度确保“发出请求”——这是埋点在页面销毁前最后机会的关键保障。
为什么 sendBeacon 适合发 DOM 状态埋点
DOM 状态(如表单填写进度、滚动位置、未提交的编辑内容)往往只在页面生命周期末期才有意义。传统 fetch 或 XMLHttpRequest 在 beforeunload / unload 中会被浏览器静默终止;而 sendBeacon:
- 使用 HTTP POST,支持发送 Blob、ArrayBuffer、FormData、URLSearchParams
- 由浏览器底层调度,在页面不可见/卸载后仍可继续发送(只要标签页未被强制 kill)
- 不触发 CORS 预检,无需服务端额外配置 Access-Control-Allow-Headers
- 不会影响页面卸载性能,无回调、不等待响应
正确捕获并序列化 DOM 状态
不能依赖卸载时实时查询 DOM(可能已开始销毁),应在状态变更时缓存,或在卸载事件中**立即快照**:
- 监听 input/scroll/change 等事件,增量更新一个全局状态对象(如
window.__lastDomState) - 在
beforeunload中调用一次快照函数,提取关键字段:当前 URL、document.title、window.scrollY、textarea.value、input:checked等 - 避免深克隆整个 DOM 树;只取业务需要的字段,控制 payload ≤ 64KB(sendBeacon 限制)
可靠调用 sendBeacon 的实践要点
常见错误是只监听 unload,但该事件在部分场景(如 iOS Safari 切后台)中不触发。应组合使用:
- 监听
beforeunload:此时 DOM 完整,可安全读取状态并调用sendBeacon(url, data) - 同时监听
pagehide(含persisted: true场景):覆盖浏览器缓存页、安卓 PWA 后台等 case - 避免重复发送:用
sessionStorage或标志位记录是否已触发,防止多个事件连续触发 - data 推荐用
URLSearchParams(兼容性好、体积小)或JSON.stringify()+Blob(需指定 type="application/json")
服务端与容错建议
sendBeacon 不返回 Promise,无法知道是否送达。需从两端增强鲁棒性:
- 服务端:接收端必须幂等;建议加
timestamp和pageId(如performance.now() + Math.random())避免重复计费或统计 - 前端兜底:对非关键埋点,可配合 visibilitychange + setTimeout 延迟 300ms 发送普通 fetch(仅当页面仍可见)
- 监控:在开发环境监听
navigator.sendBeacon返回值(true/false),false 表示队列满或参数非法,可上报异常
不复杂但容易忽略:状态采集要早、发送要快、数据要轻、服务端要稳。sendBeacon 不是万能锁,而是最后一程的可靠信使。










