sendbeacon 是异步无响应的 post 请求,保证页面卸载前发出但不保证送达,仅支持三种 mime 类型且 payload 受限(通常 64kb),不阻塞卸载、无回调,不可用于强一致性场景。

理解 sendBeacon 的核心约束
sendBeacon 发送的是异步、无响应的 POST 请求,浏览器保证它在页面卸载(包括刷新、关闭、导航)前被发出,但不保证服务器是否收到或处理成功。它只支持 text/plain、application/x-www-form-urlencoded、application/json 三类 MIME 类型,且 payload 大小受浏览器限制(通常约 64KB,Chrome 实测上限约 256KB,但不可依赖)。关键点是:它不阻塞卸载流程,也不提供回调,因此不能用于需要确认的强一致性场景。
构造最小可行快照数据
表单快照应仅包含真正需要恢复的关键字段,避免序列化整个 DOM 或冗余状态。推荐结构:
- 提取用户显式输入的
<input>、<textarea></textarea>、<select></select>值(排除 type="hidden" 或 disabled 字段) - 记录时间戳(
Date.now())、当前 URL、表单唯一 ID(如 form.dataset.id) - 用
JSON.stringify()序列化为紧凑 JSON 字符串,避免换行和空格 - 若字段值过大(如富文本),可截断或哈希摘要,而非全量发送
绑定卸载时机并降级兜底
仅靠 beforeunload 不可靠(可能被阻止或不触发),需组合多个事件:
- 监听
visibilitychange:当document.visibilityState === 'hidden'时触发快照发送 - 监听
pagehide:比beforeunload更可靠,且能捕获安卓后台、iOS 切换 App 等场景 - 在
unload事件中再发一次(作为最后尝试,尽管部分浏览器已禁用该事件的网络请求) - 对不支持 sendBeacon 的旧浏览器(如 IE),退回到同步 XHR(
xhr.open(..., false)),但仅限同源且需用户交互后才允许(否则会被阻止)
服务端配合与幂等设计
因 sendBeacon 可能重复触发(如快速刷新、多标签操作),服务端必须做到幂等:
- 客户端在快照中附带唯一 request ID(如
crypto.randomUUID()或时间戳+随机数) - 服务端用该 ID 做去重缓存(如 Redis 10 分钟 TTL),或直接覆盖同表单 ID 的最新快照
- 响应状态码始终返回 204(No Content),避免客户端误判失败而重试
- 不依赖响应体,不解析响应内容——sendBeacon 本就不读取响应
验证与可观测性建议
无法在前端验证发送结果,但可通过间接方式增强信心:
- 在开发环境开启
navigator.sendBeacon调用日志(含 URL、data.length),配合 Network 面板观察请求是否发出 - 服务端记录接收日志(含 User-Agent、Referer、request ID),用于排查低版本浏览器或拦截问题
- 对高价值表单,可在用户下次进入时主动拉取最近快照(带时间戳比对),实现“软恢复”而非强同步










