sendBeacon 在页面卸载时不能保证 100% 成功,但它是目前唯一被浏览器明确支持、在 beforeunload / pagehide / unload 阶段仍有机会完成的异步发送机制,设计为“尽力而为”,仅保证数据加入后台队列而非送达。

sendBeacon 在页面卸载时真的能发出去吗
不能保证 100% 成功,但它是目前唯一被浏览器明确支持、在 beforeunload / pagehide / unload 阶段仍有机会完成的异步发送机制。XMLHttpRequest 和 fetch() 在这些阶段会被浏览器中止,而 navigator.sendBeacon() 被设计为“尽力而为”:只要页面还没完全销毁、底层网络栈尚可工作,它就会尝试把数据交给浏览器后台队列发送。
- 浏览器只保证将数据加入发送队列,不提供成功/失败回调
- 数据必须是
Blob、ArrayBuffer、FormData或字符串(不能是 JSON 对象) - 请求方法固定为
POST,无法自定义 headers(如Content-Type由浏览器根据数据类型自动设)
为什么 sendBeacon 发不出去?常见原因
最常踩的坑不是 API 用错,而是数据格式或调用时机不对:
-
sendBeacon()必须在页面可见性即将结束前调用,比如在pagehide里比unload更可靠(尤其移动端) - 传入的
data如果是 JSON 字符串,但没显式指定Content-Type: application/json,后端可能解析失败——但注意:sendBeacon()不允许手动设 header,所以得靠服务端适配或改用FormData - 数据体积过大(Chrome 建议 ≤64KB,实际极限因浏览器而异),超限会静默失败
- 使用了
JSON.stringify(obj)后直接传入,但没包裹成Blob或FormData,导致浏览器无法识别编码方式
示例(正确):
const data = new FormData();
data.append('event', 'page_exit');
data.append('duration', performance.now() - startTime);
navigator.sendBeacon('/log', data);如何提高 sendBeacon 的实际送达率
这不是一个“调用即成功”的 API,得配合降级和验证逻辑:
立即学习“前端免费学习笔记(深入)”;
- 在
visibilitychange监听document.hidden变为true时就触发,比等pagehide更早一点 - 同时注册
pagehide和beforeunload,但只执行一次(用once: true或 flag 控制) - 对关键字段(如用户 ID、时间戳)做本地缓存(
localStorage),若下一次打开页面发现有未发送日志,可补传(需服务端幂等支持) - 后端记录原始
Content-Type和 body 长度,用于排查是否被截断或类型误判
sendBeacon 和 fetch + keepalive 的区别
fetch(url, { method: 'POST', keepalive: true }) 看起来更灵活,但它:
- 仅在部分 Chromium 版本中实验性支持,Firefox 已明确不实现,Safari 无计划支持
-
keepalive仍受限于请求上下文(比如不能在unload中发起新 fetch,只能延续已有请求) - 无法绕过 CORS 预检,而
sendBeacon()对简单请求(如FormData)默认跳过预检
所以,除非你只面向最新 Chrome 且能控制服务端 CORS,否则别用 keepalive 替代 sendBeacon()。
真正难的不是调用那一行代码,是确认后端能稳定接收任意编码的二进制块,并容忍无响应、无重试、无错误反馈的“黑盒传输”。



















