判断用户是否离开当前页面主要有以下几种方法,每种方法有不同的适用场景和优缺点
1. visibilitychange 事件
当用户切换标签页、最小化窗口或离开浏览器时触发
javascript
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
// 页面隐藏(用户离开)
console.log('用户离开了页面');
} else {
// 页面可见(用户返回)
console.log('用户返回了页面');
}
});
优点: 标准API,兼容性好(IE10+) 性能消耗小 准确判断标签页切换 支持移动设备
缺点: 无法判断浏览器关闭或电脑休眠 用户可能在同一个标签页内操作其他应用
2. beforeunload 事件
用户关闭标签页、刷新页面或导航到其他页面时触发。
javascript
window.addEventListener('beforeunload', function(e) {
// 可以显示确认对话框(某些浏览器限制自定义消息)
e.preventDefault();
e.returnValue = ''; // Chrome等现代浏览器要求设置returnValue
return null; // 兼容老版本
});
优点: 能捕获页面关闭/刷新 可以阻止离开(在某些浏览器中)
缺点: 不能用于发送异步请求(浏览器可能不等待) 用户体验较差(弹出确认框) 移动端支持有限 某些浏览器限制自定义消息
3. pagehide 事件
类似beforeunload,但更现代。
javascript
window.addEventListener('pagehide', function() {
// 发送数据到服务器
navigator.sendBeacon('/api/log-exit', data);
});
优点: 支持sendBeacon,适合发送离开数据 比beforeunload更可靠
缺点: IE10+支持,但老版本IE不支持
4. unload 事件
javascript
window.addEventListener('unload', () => {
// 页面即将卸载
});
优点: 明确表示页面卸载
缺点: 现代浏览器中异步请求可能被取消 影响 bfcache 不推荐用于发送请求
5. Beacon API(navigator.sendBeacon)
javascript
window.addEventListener('pagehide', () => {
navigator.sendBeacon('/api/log', data);
});
优点: 专为页面卸载时发送数据设计 异步发送,可靠且不阻塞页面卸载 不受 bfcache 影响
缺点: 只能发送少量数据(通常限制在 64KB) 无法接收服务器响应 需要后端配合接收数据
6. 心跳检测(Heartbeat)
javascript
// 客户端
let lastActive = Date.now();
setInterval(() => {
fetch('/api/heartbeat', {
method: 'POST',
keepalive: true
});
}, 30000); // 每30秒发送一次
// 监听用户活动
document.addEventListener('mousemove', updateLastActive);
document.addEventListener('keypress', updateLastActive);
function updateLastActive() {
lastActive = Date.now();
}
优点: 最准确,能判断真实离开 不受浏览器标签页切换影响
缺点: 需要服务器支持 增加网络负载 实时性较差
7. 关闭前发送同步请求
javascript
window.addEventListener('beforeunload', () => {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/log', false); // 同步请求
xhr.send(data);
});
优点: 可确保请求发送
缺点: 阻塞页面卸载,影响用户体验 现代浏览器可能限制同步请求 不推荐使用
总结与建议

推荐方案
1. 需要提示用户保存数据
javascript
window.addEventListener('beforeunload', (e) => {
if (hasUnsavedChanges) {
e.preventDefault();
e.returnValue = '';
}
});
2. 需要在离开时上报数据
javascript
window.addEventListener('pagehide', () => {
navigator.sendBeacon('/api/log', analyticsData);
});
3. 需要暂停页面资源
javascript
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
videoElement.pause();
}
});
4. 单页应用(SPA)的路由离开:
使用路由守卫(如 Vue Router 的 beforeEach,React Router 的 useBlocker)
5. 组合使用
javascript
// 页面隐藏时先尝试上报
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
sendAnalytics();
}
});
// 页面卸载时用 Beacon 补发
window.addEventListener('pagehide', () => {
navigator.sendBeacon('/api/log', finalData);
});
注意事项
-
避免在卸载事件中执行耗时操作
-
优先使用 visibilitychange 和 pagehide,避免使用 beforeunload 除非必要
-
数据上报优先使用 Beacon API,其次考虑 fetch 的 keepalive 选项
-
单页应用应结合路由生命周期进行状态管理
