You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

浏览器标签关闭时无法设置用户离线状态的技术求助

关闭浏览器标签页时设置用户离线状态的解决方案

核心方案:用navigator.sendBeacon()发送离线请求

浏览器的beforeunload/unload事件之所以不稳定,是因为浏览器为了提升卸载速度会跳过部分同步操作。而navigator.sendBeacon()是专门为页面卸载场景设计的异步请求API——它不会阻塞页面关闭,也不会弹出确认退出对话框,能把请求加入浏览器的发送队列,大概率保证请求成功送达。

前端代码示例

// 监听页面卸载前事件
window.addEventListener('beforeunload', () => {
  // 构造请求数据,替换成你的实际用户标识和接口参数
  const formData = new FormData();
  formData.append('user_id', '当前登录用户ID');
  formData.append('action', 'set_offline');

  // 发送离线请求到PHP接口
  navigator.sendBeacon('/your-php-endpoint.php', formData);
});

PHP接口处理

在your-php-endpoint.php中接收请求,调用你已定义的offline()方法:

<?php
// 验证用户身份(比如通过session或token)
session_start();
if (!isset($_SESSION['user_id'])) {
  exit;
}

$user_id = $_SESSION['user_id'];
if ($_POST['action'] === 'set_offline') {
  // 调用你已定义的offline方法
  offline($user_id);
}
?>

兜底方案:心跳检测+后端超时标记

sendBeacon并非100%可靠(比如浏览器突然崩溃、网络断连),所以需要配合心跳机制做兜底:

  • 前端定期发送心跳请求,更新用户的最后活跃时间;
  • 后端通过定时任务,把超过活跃阈值的用户标记为离线。

前端心跳代码

let heartbeatTimer;

// 发送心跳请求
function sendHeartbeat() {
  const formData = new FormData();
  formData.append('user_id', '当前登录用户ID');
  formData.append('action', 'heartbeat');
  navigator.sendBeacon('/your-php-endpoint.php', formData);
}

// 页面可见时,每30秒发一次心跳
function startHeartbeat() {
  heartbeatTimer = setInterval(sendHeartbeat, 30000);
}

// 页面隐藏时,延长心跳间隔到1分钟,减少请求
function adjustHeartbeatOnVisibility() {
  document.addEventListener('visibilitychange', () => {
    clearInterval(heartbeatTimer);
    if (document.hidden) {
      heartbeatTimer = setInterval(sendHeartbeat, 60000);
    } else {
      // 页面重新显示时,主动标记在线
      const formData = new FormData();
      formData.append('user_id', '当前登录用户ID');
      formData.append('action', 'set_online');
      navigator.sendBeacon('/your-php-endpoint.php', formData);
      startHeartbeat();
    }
  });
}

// 初始化
startHeartbeat();
adjustHeartbeatOnVisibility();

后端定时任务示例

写一个PHP脚本,配合服务器的定时任务(比如Linux的crontab)每分钟执行一次,清理超时用户:

<?php
// 连接数据库
$pdo = new PDO('mysql:host=localhost;dbname=your_database', 'username', 'password');

// 设置超时阈值:超过1分钟未发送心跳,标记为离线
$timeout = time() - 60;

// 更新状态
$stmt = $pdo->prepare("UPDATE users SET status = 'offline' WHERE last_active < :timeout AND status = 'online'");
$stmt->execute([':timeout' => $timeout]);
?>

额外注意点

  • navigator.sendBeacon仅支持POST请求,数据格式推荐用FormData,无需自定义请求头;如果需要自定义头,可以用fetch并添加keepalive: true参数,但sendBeacon的兼容性和可靠性更好。
  • 本地测试时,浏览器可能会有卸载优化,建议在线上环境或真实服务器上验证效果。
  • 绝对不要在卸载事件中使用同步AJAX请求,会阻塞页面卸载,甚至触发浏览器的退出确认弹窗。

内容的提问来源于stack exchange,提问作者anvalon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:31:35