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

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);
}

三、额外的优化建议

  1. 路由切换场景处理:如果用户是通过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);
    }
  }
}
  1. 后端兜底机制:就算前端请求失败(比如用户断网),建议后端加个定时清理任务,比如每5分钟检查一次等待列表里的游戏,移除那些超过一定时间没有用户在线的项(可以通过Socket.io的连接状态来判断用户是否在线)。

备注:内容来源于stack exchange,提问作者eligolf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 11:58:08