sendBeacon仅负责可靠发送,不处理耗时计算或大数据;真正问题在于未将数据采集、聚合、序列化前置到页面活跃期完成,导致beforeunload阶段卡顿或失败。
不能直接用 navigator.sendBeacon 发送“耗时巨大”的埋点数据——它只负责可靠发出,不负责处理大体积、高延迟或需前置计算的数据。 真正的问题是:你把“数据生成耗时”和“上报通道不可靠”混为一谈了。先做计算,再用 sendBeacon 发;而不是指望它帮你扛住 CPU 或网络等待。
navigator.sendBeacon 是一个纯发送接口,它不做序列化、不压缩、不重试、不等待服务端响应。如果你在 beforeunload 里才开始 JSON 序列化一个 10MB 的用户行为快照,或调用一个同步加密函数,那卡顿和失败都发生在 sendBeacon 调用之前,跟它无关。
beforeunload 后立即冻结 JS 执行上下文(尤其在 Chrome 80+),长任务大概率被中断sendBeacon 只接受 Blob 或 ArrayBuffer,传入字符串会自动转成 UTF-8 Blob,但不会帮你做任何预处理核心思路是:**埋点数据的采集、聚合、序列化,必须在页面活跃期完成并缓存;beforeunload 里只做最后的轻量发送。**
visibilitychange:当页面切到后台(最小化/切标签)时,立刻触发一次预聚合,把当前积压数据序列化好,存在 sessionStorage 或内存变量里requestIdleCallback(配合 setTimeout 降级)在空闲帧里做重计算,避免阻塞交互Web Worker 处理好,主线程只存最终 Blob 引用即使数据已准备好,错误的调用方式仍会导致失败:
beforeunload 里循环调用 sendBeacon —— 浏览器对并发 Beacon 请求有限制(通常 ≤ 6),应先合并数据再发一次Blob 的 type 匹配后端接收逻辑:application/json 和 application/x-www-form-urlencoded 在服务端解析行为完全不同JSON.stringify 直接传对象:它可能包含 undefined、function 或循环引用,导致静默失败;务必先用 JSON.stringify(data, (k, v) => v === undefined ? null : v) 安全序列化if (!navigator.sendBeacon(url, blob)) { /* fallback: 尝试 img 请求 */ },虽然失败不可挽回,但可记录日志用于监控IE 或某些 WebView 不支持 sendBeacon,此时同步 XMLHttpRequest 已被现代浏览器禁止(Chrome 80+ 会直接报错 Failed to execute 'send' on 'XMLHttpRequest': Synchronous XMLHttpRequest on the main thread is not allowed)。
new Image().src = url + '?' + new URLSearchParams(data).toString()
GET /track?... 和 POST /track(sendBeacon 发的是 POST)两种入口最常被忽略的一点:你认为“巨大”的数据,往往源于没做采样或没设上限。比如滚动深度埋点,每 10px 记一次,5 分钟就产生上万条——这种数据根本不该全量上报,而应在前端按时间窗口聚合(如“过去 30 秒最大滚动深度”)。sendBeacon 可靠,但救不了设计缺陷。