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

Laravel+React系统关闭浏览器标签后更新用户离线状态的实现求助

解决浏览器关闭时无法更新用户离线状态的问题

问题根源

beforeunload事件触发时,浏览器正处于页面卸载流程中,常规的异步await请求会因页面被终止而无法完成,导致状态更新请求无法送达后端。

可行解决方案

1. 使用Navigator.sendBeacon API(推荐)

这是浏览器专为页面卸载场景设计的API,会在后台异步发送请求,浏览器会确保请求完成后再终止页面,不会被卸载流程打断。

修改前端代码:

const updateStatus = async (status) => {
    try {
        await api.post('/api/user/status', { status });
    } catch (error) {
        console.error('更新状态失败:', error);
    }
};

// 页面加载时设为在线
window.addEventListener('load', () => {
    updateStatus('online');
});

// 页面卸载时用Beacon发送离线请求
window.addEventListener('beforeunload', () => {
    // 构造FormData,适配Laravel的请求解析
    const formData = new FormData();
    formData.append('status', 'disconnected');
    // 带上CSRF Token(Session认证场景)
    formData.append('_token', document.querySelector('meta[name="csrf-token"]').content);
    // 如果是API Token认证,将Token加入FormData
    // formData.append('api_token', localStorage.getItem('api_token'));
    
    navigator.sendBeacon('/api/user/status', formData);
});

注意:如果是无状态API认证(如JWT),由于sendBeacon无法自定义请求头,需要将Token放在FormData中,后端需调整用户认证逻辑:

// 后端updateStatus方法修改认证逻辑(示例)
public function updateStatus(Request $request)
{
    $request->validate([
        'status' => 'required|string|in:online,disconnected,away'
    ]);

    // API Token认证场景下获取用户
    $user = User::where('api_token', $request->api_token)->first();
    // Session认证则保持原auth()->user()即可
    // $user = auth()->user();

    $user->chat_status = $request->status;
    $user->save();

    return response()->json(['message' => '状态更新成功']);
}

2. 同步XMLHttpRequest(不推荐)

通过同步请求强制等待请求完成,但会阻塞页面卸载,影响用户体验,部分浏览器已限制该用法:

window.addEventListener('beforeunload', () => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/api/user/status', false); // false为同步模式
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.setRequestHeader('X-CSRF-TOKEN', document.querySelector('meta[name="csrf-token"]').content);
    // API Token认证需添加Authorization头
    // xhr.setRequestHeader('Authorization', 'Bearer ' + localStorage.getItem('api_token'));
    xhr.send(JSON.stringify({ status: 'disconnected' }));
});

3. 心跳机制兜底(更可靠)

仅依赖卸载请求存在失败风险(如浏览器崩溃),搭配心跳机制可实现双重保障:

前端定时发送心跳

let heartbeatInterval;

window.addEventListener('load', () => {
    updateStatus('online');
    // 每30秒发送一次在线心跳
    heartbeatInterval = setInterval(() => {
        updateStatus('online');
    }, 30000);
});

window.addEventListener('beforeunload', () => {
    const formData = new FormData();
    formData.append('status', 'disconnected');
    formData.append('_token', document.querySelector('meta[name="csrf-token"]').content);
    navigator.sendBeacon('/api/user/status', formData);
    clearInterval(heartbeatInterval);
});

后端定时清理超时用户

  1. 给用户表添加last_active_at字段(记录最后活跃时间)
  2. 修改后端updateStatus方法更新活跃时间:
public function updateStatus(Request $request)
{
    $request->validate([
        'status' => 'required|string|in:online,disconnected,away'
    ]);

    $user = auth()->user();
    $user->chat_status = $request->status;
    $user->last_active_at = now(); // 更新最后活跃时间
    $user->save();

    return response()->json(['message' => '状态更新成功']);
}
  1. 创建Laravel定时任务,清理超过5分钟未活跃的用户:
// 生成命令:php artisan make:command CleanupInactiveUsers
namespace App\Console\Commands;

use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;

class CleanupInactiveUsers extends Command
{
    protected $signature = 'status:cleanup';
    protected $description = '将长时间未活跃的用户状态设为离线';

    public function handle()
    {
        User::where('last_active_at', '<', Carbon::now()->subMinutes(5))
            ->where('chat_status', '!=', 'disconnected')
            ->update(['chat_status' => 'disconnected']);
        
        $this->info('已完成离线状态清理');
    }
}
  1. 在app/Console/Kernel.php中配置定时任务:
protected function schedule(Schedule $schedule)
{
    $schedule->command('status:cleanup')->everyMinute();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 18:41:04