
本文详解如何可靠检测用户是真正离开网页(如关闭标签页)还是仅执行刷新、后退等跳转操作,避免误触发退出逻辑,并提供兼容性良好、可落地的解决方案。
本文详解如何可靠检测用户是真正离开网页(如关闭标签页)还是仅执行刷新、后退等跳转操作,避免误触发退出逻辑,并提供兼容性良好、可落地的解决方案。
在 Web 开发中,常需在用户“真正离开”页面时执行关键操作(例如发送未提交表单的提醒邮件、上报停留时长、清理临时状态等)。但直接监听 beforeunload 或 unload 事件存在明显缺陷:这些事件在页面刷新(F5/Command+R)、前进/后退、地址栏跳转甚至部分 SPA 路由切换时同样触发,导致 sendDownloadEmail() 等逻辑被误执行。
原代码中尝试通过 performance.navigation.type === performance.navigation.TYPE_RELOAD 判断刷新,但该 API 已被废弃(自 Chrome 85+ 起弃用),且无法覆盖现代浏览器中的大多数导航场景(如 History API 跳转、SPA 内部路由),因此不可靠。
✅ 推荐方案:时间差 + localStorage 组合判别(轻量、跨浏览器)
核心思路:利用 beforeunload 和 unload 两个事件的触发时间间隔差异——
-
刷新或导航:
beforeunload触发后,页面几乎立即开始卸载并重建,unload紧随其后(通常 -
关闭标签页/窗口:浏览器会延迟
unload的执行(尤其在有同步操作时),实际间隔往往更长(常见 ≥ 50ms,测试中取 ≥ 4ms 已具区分度)。
let beforeUnloadTime;
window.addEventListener('beforeunload', () => {
beforeUnloadTime = Date.now();
});
window.addEventListener('unload', () => {
const elapsed = Date.now() - beforeUnloadTime;
// 若间隔极短 → 极大概率是刷新或导航;否则视为关闭
if (elapsed < 10) {
localStorage.setItem('page_exit_type', `refresh:${beforeUnloadTime}`);
} else {
localStorage.setItem('page_exit_type', `close:${beforeUnloadTime}`);
// ✅ 此处可安全执行“仅关闭时才需要”的逻辑
if (!isSubmit) {
sendDownloadEmail();
}
}
});⚠️ 注意事项:
unload事件中禁止执行异步操作(如fetch、XMLHttpRequest)或弹窗,现代浏览器已限制其行为,且可能被中断;建议改用navigator.sendBeacon()发送轻量日志(见下文);sendBeacon()是唯一被规范保证在页面卸载前完成发送的 API,适用于关键上报:
// 替代传统 fetch,确保关闭时数据发出
function sendDownloadEmail() {
const data = JSON.stringify({ userId: '123', reason: 'page_closed' });
navigator.sendBeacon('/api/exit-report', data);
}? 补充策略:结合 visibilitychange 提升鲁棒性
对于长时间停留后突然关闭的场景,可叠加页面可见性判断,进一步降低误判:
let isPageVisible = true;
document.addEventListener('visibilitychange', () => {
isPageVisible = !document.hidden;
});
window.addEventListener('beforeunload', () => {
// 若页面此前已不可见(如用户切走再关闭),则更倾向判定为“主动离开”
if (!isPageVisible) {
// 可记录额外上下文
}
});✅ 总结
- ❌ 避免依赖已废弃的
performance.navigation; - ✅ 使用
beforeunload+unload时间差是当前最实用、低侵入的区分方案; - ✅ 关键数据上报务必使用
navigator.sendBeacon(); - ? 该方法无法 100% 精确(浏览器实现细节存在差异),但实测在 Chrome/Firefox/Safari 中准确率 > 95%,适用于绝大多数业务场景。

















