
本文介绍如何利用页面可见性 API(visibilitychange)配合 sendBeacon,在用户关闭标签页、切换窗口或导航离开时可靠触发数据保存,避免因 change 事件未触发导致的最后输入丢失问题。
本文介绍如何利用页面可见性 api(visibilitychange)配合 sendbeacon,在用户关闭标签页、切换窗口或导航离开时可靠触发数据保存,避免因 `change` 事件未触发导致的最后输入丢失问题。
在构建以用户体验为中心的表单应用(如工时填报系统)时,常采用「实时保存」策略:监听 <input> 的 change 事件,一旦用户失焦即提交变更。但该方案存在明显盲区——当用户在最后一个文本框中输入后直接关闭浏览器标签页、按 Alt+Tab 切换窗口,或点击地址栏跳转,输入框从未触发 blur 或 change,导致最新数据彻底丢失。
此时,beforeunload 事件看似是直觉选择,但它已被现代浏览器严格限制:不允许执行异步操作(如 AJAX 请求),且会强制弹出不可自定义的确认对话框,严重影响体验。更优解是使用 Page Visibility API 配合 navigator.sendBeacon()。
visibilitychange 事件在页面可见性状态改变时触发(例如标签页被隐藏、最小化、切换至后台),且不阻塞页面卸载流程。而 sendBeacon() 是专为“页面即将销毁”场景设计的异步、无响应、高优先级的 POST 请求机制——即使页面已开始卸载,浏览器仍会尽力将数据发送至服务器。
以下是一个生产就绪的实现示例:
// 收集待保存的表单数据(可根据实际结构调整)
function collectFormData() {
const data = {};
document.querySelectorAll('input[type="text"], input[type="number"]').forEach(el => {
if (el.value.trim()) {
data[el.name || el.id] = el.value;
}
});
return JSON.stringify(data);
}
// 监听页面隐藏事件
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const payload = collectFormData();
// sendBeacon 自动设置 Content-Type: text/plain;如需 application/json,请用 Blob 包装
const blob = new Blob([payload], { type: 'application/json' });
navigator.sendBeacon('/api/save-hours', blob);
}
});✅ 关键优势:
- 无弹窗干扰,完全静默执行;
- 兼容主流浏览器(Chrome 52+、Firefox 59+、Edge 79+、Safari 16.4+);
-
sendBeacon请求独立于页面生命周期,成功率远高于普通fetch或XMLHttpRequest。
⚠️ 注意事项:
-
sendBeacon()仅支持POST方法,且无法读取响应;若需服务端返回校验结果(如冲突检测),应结合定时轮询或 WebSocket 补充实现; - 不要依赖
visibilitychange做唯一保存通道——仍需保留change/input实时保存逻辑,visibilitychange仅作为兜底; - Safari 对
sendBeacon的请求体大小有限制(通常 ≤ 64KB),请确保 payload 精简; - 若需兼容老旧浏览器(如 IE),可降级为
beforeunload+ 同步 XHR(仅限简单日志记录,且需用户确认)。
综上,通过 visibilitychange + sendBeacon 组合,你能在不增加 UI 按钮、不牺牲交互流畅性的前提下,显著提升数据持久化的鲁棒性,真正实现「所见即所存」。

















