Vue中监听窗口/标签页关闭事件并调用API移除用户等待游戏项的实现咨询
Vue中监听窗口/标签页关闭事件并调用API移除用户等待游戏项的实现咨询
嘿,这个需求在多人游戏场景里太实用了,刚好适配你的Vue+Express+MongoDB技术栈,我来给你梳理下靠谱的实现方案:
一、在Vue组件中监听窗口关闭相关事件
首先你需要在负责等待列表的Vue组件(或者全局的App.vue里)监听浏览器的两个关键事件:
beforeunload:当用户尝试关闭窗口、刷新页面或者导航离开时触发,可以用来给用户弹出确认提示unload:页面实际卸载时触发,这时候我们要调用你的删除API
在Vue组件里可以这么写:
<template> <!-- 你的游戏等待列表UI --> </template> <script> export default { data() { return { userId: localStorage.getItem('currentUserId') // 替换成你实际获取用户ID的方式,比如Vuex、接口返回 } }, mounted() { // 挂载时添加事件监听 window.addEventListener('beforeunload', this.handleBeforeUnload) window.addEventListener('unload', this.handleUnload) }, beforeDestroy() { // 组件销毁时移除监听(路由切换时有用,页面关闭时可能不会触发) window.removeEventListener('beforeunload', this.handleBeforeUnload) window.removeEventListener('unload', this.handleUnload) }, methods: { handleBeforeUnload(e) { // 给用户提示确认关闭,注意:部分浏览器会忽略自定义文本,显示默认提示 e.preventDefault() e.returnValue = '离开后你的等待游戏会被移除,确定要走吗?' }, async handleUnload() { // 这里调用删除API,重点要保证请求能在页面卸载时完成 try { await fetch(`/api/waiting/remove/user/${this.userId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', // 如果你的接口需要认证,比如JWT,记得带上授权头 'Authorization': `Bearer ${localStorage.getItem('authToken')}` }, keepalive: true // 核心:让浏览器在页面卸载后继续完成这个请求 }) } catch (err) { console.error('移除等待游戏失败:', err) } } } } </script>
二、为什么要用keepalive: true?
普通的异步请求(比如axios默认配置)在页面卸载时会被浏览器中断,导致你的Express接口根本收不到请求。而fetch的keepalive选项是专门为这种场景设计的,它会告诉浏览器:这个请求很重要,就算页面关了也要把它发出去。
如果你更习惯用axios,也可以通过navigator.sendBeacon实现(不过sendBeacon只支持POST请求,需要你后端加一个对应的POST路由来处理删除逻辑):
// Express新增POST路由 router.post("/waiting/remove/user", async (req, res) => { const { userId } = req.body; // 下面的逻辑和你现有DELETE接口完全一样 try { const deletedGames = await WaitingGame.deleteMany({ userId }); if (deletedGames.deletedCount > 0) { io.emit("waitingListUpdate"); res.status(200).json({ message: "All waiting games for the user removed successfully." }); } else { res.status(200).json({ message: "No waiting games found for the user, but request was successful." }); } } catch (err) { res.status(500).json({ message: "Error removing waiting games.", error: err }); } });
然后Vue里的handleUnload改成:
handleUnload() { const formData = new FormData(); formData.append('userId', this.userId); navigator.sendBeacon('/api/waiting/remove/user', formData); }
三、额外的优化建议
- 路由切换场景处理:如果用户是通过Vue路由切换离开当前页面(不是关闭窗口),可以在
beforeRouteLeave钩子里面调用删除接口,这样更及时:
beforeRouteLeave(to, from, next) { // 调用删除API this.removeUserFromWaitingList(); next(); }, methods: { async removeUserFromWaitingList() { try { await axios.delete(`/api/waiting/remove/user/${this.userId}`); // 这里可以手动触发Socket.io的更新,或者等后端emit } catch (err) { console.error('移除失败:', err); } } }
- 后端兜底机制:就算前端请求失败(比如用户断网),建议后端加个定时清理任务,比如每5分钟检查一次等待列表里的游戏,移除那些超过一定时间没有用户在线的项(可以通过Socket.io的连接状态来判断用户是否在线)。
备注:内容来源于stack exchange,提问作者eligolf
相关产品推荐
相关产品推荐

