Vue 3 中需组合使用 onBeforeRouteLeave、表单脏状态管理与 beforeunload 监听实现完整页面离开拦截:前者处理路由跳转,后者兜底刷新/关页,配合深比较判断真实改动并及时清理监听器。

在 Vue 3 单文件组件中实现页面离开时拦截并弹窗提示未保存数据,核心是组合使用 组件内导航守卫 + 表单脏状态管理 + beforeunload 兜底监听。只靠其中一项无法覆盖所有离开场景(比如刷新、关页、地址栏跳转),必须三者配合。
用 onBeforeRouteLeave 拦截路由跳转
这是最直接的入口,仅对 Vue Router 控制的导航生效(如点击 router-link、编程式导航):
- 在
setup()中调用onBeforeRouteLeave,传入守卫函数 - 不要直接
return $confirm().then(...)—— 守卫是同步执行的,Promise 不会被等待 - 正确做法:先调用
next(false)阻止默认跳转 → 显示 UI 框架弹窗(如 Element Plus 的$confirm)→ 用户点“确定”再调用next(),点“取消”不操作即可 - 加防重复触发标志位,避免用户连点返回按钮导致多个弹窗叠加
准确判断“是否真有未保存改动”
不能只靠输入事件简单设 flag,否则空格、大小写变化也会误判:
从零开始编排 SpringBoot+Vue 全栈项目工作流,适用于新建全栈项目或完整脚手架请求,触发后依次调用 product‑spec‑builder(需求调研)等模块。
- 组件初始化时,用
structuredClone或lodash/isEqual保存原始表单数据快照 - 监听表单项变更(
@input/@change/watch表单对象),用深比较对比当前值与初始值 - 仅当深比较结果为
false(即真正不同)时,才设isDirty.value = true - 提交成功、重置表单、或点击“取消编辑”后,必须同步将
isDirty和pendingSave置为false
用 beforeunload 拦截刷新和关页
onBeforeRouteLeave 对关闭标签页、按 F5 刷新、地址栏输新网址完全无效,必须靠浏览器原生机制兜底:
立即学习“前端免费学习笔记(深入)”;
- 当
isDirty.value变为true时,立即执行window.addEventListener('beforeunload', handler) - handler 中只需返回任意字符串(如
return '数据未保存,确定要离开?'),现代浏览器会统一显示标准提示框 - 一旦用户完成保存、放弃编辑或跳转到其他页面,必须立刻移除该监听器,防止内存泄漏和误提示
- 注意:不能在
beforeunload中调用异步操作(如 API 请求或自定义弹窗),它只支持同步返回提示语
弹窗体验要稳,不能闪、不能漏、不能卡
UI 框架弹窗(如 ElMessage、$confirm)是异步的,和导航守卫的执行时机容易冲突:
- 避免在守卫里直接调用弹窗后立即
next(),会导致跳转和弹窗不同步 - 推荐用
setTimeout(() => { showConfirm() }, 0)做微任务延迟,让守卫先完成同步流程 - 弹窗打开期间设
isConfirming = true,后续再次触发守卫直接忽略,防止快速连点弹出多个对话框 - 若用户在弹窗打开时强制刷新或关页,
beforeunload会接管并提示,形成双重保障

















