Vue.js/Quasar框架如何监听浏览器关闭事件上报用户离线状态
Vue.js/Quasar 监听浏览器关闭上报离线状态实现方案
浏览器没有能100%覆盖所有场景的关闭事件钩子,组合原生事件+专用上报API+后端兜底可以覆盖绝大多数常规使用场景,具体实现如下:
核心事件选型
不要用老旧的unload事件,现代浏览器会强制拦截该事件内的异步请求,上报成功率极低,优先组合以下三个事件:
beforeunload:关闭标签页、关闭浏览器、主动刷新、跳转外站时触发,是最贴近页面关闭时机的钩子pagehide:现代浏览器标准的页面卸载事件,移动端兼容性优于beforeunloadvisibilitychange:兜底事件,覆盖页面切后台、最小化、移动端浏览器直接滑掉进程的部分场景
上报请求注意事项
普通的axios/fetch异步请求会在页面卸载时被浏览器直接终止,必须用浏览器专门为卸载场景设计的navigator.sendBeacon()接口发请求:
- 该接口会将请求加入浏览器持久化任务队列,即使页面立即关闭也会保证请求发出
- 请求默认异步发送,不会阻塞页面关闭/跳转,无用户体验损耗
- 除了极个别年代久远的浏览器,全球98%以上的用户环境都支持该接口,无需额外兼容
框架内具体实现
Quasar 框架实现
在src/boot目录下新建全局状态监听的boot文件,全局绑定事件即可,无需在单个业务组件重复绑定:
// src/boot/user-presence.js import { boot } from 'quasar/wrappers' export default boot(() => { const reportOffline = () => { // 替换为自身业务需要的上报字段 const payload = JSON.stringify({ userId: localStorage.getItem('currentUid'), status: 'offline', reportTime: Date.now() }) // 替换为自身后端的状态更新接口 navigator.sendBeacon('/api/user/update-status', payload) } const handleBeforeUnload = (e) => { reportOffline() // 如需弹出关闭确认提示,放开下面两行注释即可 // e.preventDefault() // e.returnValue = '' } const handlePageHide = () => reportOffline() const handleVisibilityChange = () => { if (document.visibilityState === 'hidden') reportOffline() } // 挂载全局监听 window.addEventListener('beforeunload', handleBeforeUnload) window.addEventListener('pagehide', handlePageHide) document.addEventListener('visibilitychange', handleVisibilityChange) })
写完记得在quasar.config.js的boot配置里加入这个新建的文件即可生效。
纯Vue.js 实现
直接在根组件App.vue的生命周期内绑定/解绑事件,避免内存泄漏:
<script setup> import { onMounted, onUnmounted } from 'vue' const reportOffline = () => { const payload = JSON.stringify({ userId: localStorage.getItem('currentUid'), status: 'offline', reportTime: Date.now() }) navigator.sendBeacon('/api/user/update-status', payload) } const handleBeforeUnload = (e) => { reportOffline() // 如需关闭确认提示放开下面注释 // e.preventDefault() // e.returnValue = '' } const handlePageHide = () => reportOffline() const handleVisibilityChange = () => { if (document.visibilityState === 'hidden') reportOffline() } onMounted(() => { window.addEventListener('beforeunload', handleBeforeUnload) window.addEventListener('pagehide', handlePageHide) document.addEventListener('visibilitychange', handleVisibilityChange) }) onUnmounted(() => { window.removeEventListener('beforeunload', handleBeforeUnload) window.removeEventListener('pagehide', handlePageHide) document.removeEventListener('visibilitychange', handleVisibilityChange) }) </script>
必做兜底方案
没有任何前端监听方案能覆盖极端异常场景:比如用户直接拔电源、浏览器崩溃、断网时事件根本不会触发,必须配合后端逻辑保证状态一致:
- 加心跳机制:前端每30-60秒向后端发一次心跳请求,后端连续2个周期没收到心跳就自动将对应用户置为离线
- 如果项目用了Websocket,直接监听Websocket的断连事件更新用户状态,可靠性远高于前端页面卸载事件
前端卸载事件上报只用来做即时状态更新,不要作为用户离线判断的唯一依据。
内容的提问来源于stack exchange,提问作者technobot
相关产品推荐
相关产品推荐

