当前位置: 首页 > news >正文

pagehide/beforeunload / unload / onUnmounted 执行顺序与navigator.sendBeacon使用陷阱详解

一、前言

在前端项目中,我们经常需要在用户 关闭页面、刷新页面或离开当前路由 时执行一些清理或上报操作,例如:

  • 使用 navigator.sendBeacon() 上报用户停留时间;

  • 清除定时器或全局事件;

  • 保存用户未提交的表单数据。

在实践中,很多开发者容易混淆三个事件或钩子:

  • beforeunload

  • unload

  • Vue 的 onUnmounted

它们看似相似,但触发时机、作用范围和执行顺序都不同。本文将系统梳理它们的差异,并给出最佳实践。


二、事件执行顺序对比

当浏览器关闭或刷新页面时,事件触发顺序如下:

阶段事件 / 钩子说明
beforeunload页面即将卸载,可同步执行逻辑或调用 sendBeacon
pagehide(可选)页面被隐藏或卸载,支持 bfcache
unload页面资源彻底卸载,JS 环境即将清空
onUnmounted不会触发(Vue 根组件还未卸载)

而在 SPA 内部路由切换 时:

阶段事件 / 钩子说明
beforeRouteLeave路由离开前(可中断跳转)
onUnmounted当前组件被销毁
beforeunload / unload不会触发,因为页面未关闭

三、window.onbeforeunloadaddEventListener 的区别

很多开发者会直接写:

window.onbeforeunload = () => {navigator.sendBeacon('/report')
}

问题在于:在多个组件中使用时,后定义的会覆盖前一个

// 组件 A
window.onbeforeunload = () => console.log('A')// 组件 B
window.onbeforeunload = () => console.log('B')

结果只会输出 B,因为后者覆盖了前者。

✅ 推荐写法:addEventListener

function handleBeforeUnload() {navigator.sendBeacon('/report')
}window.addEventListener('beforeunload', handleBeforeUnload)
  • 支持多个监听器

  • 不会互相覆盖

  • 兼容性好

注意:在组件卸载时需要移除监听器,防止重复绑定或内存泄漏。


四、addEventListener 要配合 onUnmounted 移除

在 Vue 组件中:

onMounted(() => {const handleUnload = () => {console.log('unload')}window.addEventListener('unload', handleUnload)
})onUnmounted(() => {window.removeEventListener('unload', handleUnload)
})

这样可以确保 SPA 内部路由切换时不会产生多余的监听器。


五、刷新页面时 onUnmounted 不触发的原因

onMounted(() => {const handleUnload = () => console.log('unload')window.addEventListener('unload', handleUnload)
})onUnmounted(() => {window.removeEventListener('unload', handleUnload)
})

在刷新页面时,仍然会触发 unload。原因是:

  • 刷新时 Vue 根实例尚未“优雅卸载”,浏览器直接进入页面卸载阶段。

  • 因此 onUnmounted 永远不会执行,监听器没有被移除,但 unload 仍然有效。

在 SPA 内部路由切换时,onUnmounted 会触发,从而移除监听器。


六、SPA 场景下的影响

场景事件触发情况说明
页面刷新 / 关闭unload 触发❌ onUnmounted 不触发监听器仍有效
SPA 内部路由切换onUnmounted 触发❌ unload 不触发如果在 onUnmounted 中移除监听器,则 unload 不再生效

结论:如果你在 mounted 中绑定了 unload 事件,并在 onUnmounted 中移除它,SPA 内路由跳转后该事件不会再触发。


七、使用 pagehide 代替 beforeunload / unload

现代浏览器提供了 pagehide 事件,推荐在页面退出埋点中使用:

window.addEventListener('pagehide', () => {navigator.sendBeacon('/report', JSON.stringify({ time: Date.now() }))
})

beforeunload / unload 对比

事件标签页切换页面刷新页面关闭后退 / 前进(同域 bfcache)后退离开当前站点(不同域)
beforeunload❌ 不触发✅ 触发
unload❌ 不触发⚠️(bfcache 下可能不触发)✅ 触发(页面真正卸载)
pagehide❌ 不触发✅ 触发(可用 event.persisted=false 判断)
  • pagehide 覆盖了刷新、关闭、后退、bfcache 等场景
  • 异步操作使用 navigator.sendBeacon() 可以安全上报

  • SPA 内部路由切换仍需 onUnmounted 做组件级清理

小结:在现代浏览器中,pagehide 已经是最可靠的页面退出事件,可以替代 beforeunloadunload


八、最佳实践:埋点或离开上报

onMounted(() => {const sendReport = () => {navigator.sendBeacon('/report', JSON.stringify({ time: Date.now() }))}window.addEventListener('pagehide', sendReport)window.addEventListener('beforeunload', sendReport) //兼容旧版本
})onUnmounted(() => {window.removeEventListener('pagehide', sendReport)window.removeEventListener('beforeunload', sendReport)
})

关键要点总结

要点建议
1不要使用 window.onbeforeunload,会被覆盖
2使用 addEventListener,并在 onUnmounted 中移除
3onUnmounted 在刷新或关闭页面时不会触发
4SPA 内部路由切换时触发 onUnmounted,此时移除监听器
5页面退出埋点推荐使用 pagehide + beforeunload,不依赖 unload

九、总结

  • onUnmounted 属于 Vue 生命周期,只在组件销毁(如 SPA 内路由切换)时触发。

  • beforeunload / unload 属于浏览器生命周期,页面关闭或刷新时才触发。

  • pagehide 是现代浏览器最可靠的页面退出事件,兼容 bfcache。

  • window.onbeforeunload 会被覆盖,不推荐使用。

  • addEventListener 更灵活,但 SPA 内路由切换时要在 onUnmounted 移除。

  • 页面退出埋点(sendBeacon)推荐放在 pagehidebeforeunload,几乎覆盖所有退出场景。

 

http://www.dtcms.com/a/602465.html

相关文章:

  • 解决若依框架点击菜单无效的问题(或者main主体白板)vue3版本
  • 回溯-22括号生成
  • 如何做网站卖衣服第一营销网
  • 怎么写网站建设的说明线上设计师是什么意思
  • 力扣(LeetCode) ——43.字符串相乘(C++)
  • 哪里有做网站服务世安建设有限网站
  • 目前哪些企业需要做网站建设的呢企业网站优化兴田德润优惠
  • strchr函数
  • 做图书网站赚钱么关于网站建设的名言
  • Xen PVH 模式启动 Dom0 配置文档
  • 26_FastMCP 2.x 中文文档之FastMCP服务端部署:HTTP 部署指南
  • cisp-pte之SQL注入题之vulnerabilities/fu1.php?id=1
  • 发布三小时,GitHub标星11K,华为内部的图解网络笔记限时开源
  • 【electron】解决CS里的全屏问题
  • 手机网站建站软件毕设做桌面软件 网站
  • 【1.8】基于FPGA的costas环开发2——解调端下变频模块
  • 电商培训机构哪家好网站优化一年多少钱
  • 专业手机网站建设设计软文案例400字
  • pyautocad 获取obb最小包围矩形后旋转平行后标注长宽
  • Google ADK、OpenAI Agents SDK 和 AgentScope的详细对比
  • 深入解析MySQL数据库报错:`ERROR 1146 (42S02) Table ‘mysql.user‘ doesn‘t exist`
  • 用C语言编写有趣程序 | 探索如何用编程创造乐趣与实用工具
  • 武城网站建设公司谷歌广告代理商
  • Docker是什么?怎么安装与配置?
  • 搭建网站的步骤wordpress地址改不了
  • 手机网站报价表网站建设规划书实训报告
  • (Linux操作系统)MySQL在Centos7环境安装和MySQL数据库基础
  • UCOS-III笔记(三)
  • 如何自己建设简单的手机网站品牌创意网站建设
  • 关于csdn隐私