onbeforerouteleave需在setup同步注册,配合深比较判断真实变更、手动控制next时机,并与beforeunload兜底配合,才能可靠拦截未保存离开行为。

在 Vue 组件中用 onBeforeRouteLeave 拦截未保存的离开行为,关键不是“弹个窗”,而是准确判断状态、合理控制流程、兼顾所有退出路径。它只管路由跳转,不管刷新或关页,所以必须配合其他机制才真正可靠。
如何正确注册并使用 onBeforeRouteLeave
这个守卫必须在组件 setup 阶段同步注册,不能放在异步回调、条件语句或事件处理函数里,否则会失效:
- Composition API 中只能通过 onBeforeRouteLeave((to, from, next) => { ... }) 注册,且需确保 import 正确(vue-router@4+)
- 不能返回 Promise 并直接 await 弹窗逻辑——浏览器不等待,会立刻放行;必须手动控制 next 调用时机
- 若表单正在提交或自动保存中,应先等待完成再决定是否放行,避免中断保存流程
怎么才算“真的改了”?别被空格和顺序骗了
仅靠 v-model 双向绑定触发 dirty 标志远远不够。用户输入空格、切换大小写、调整数组项顺序,这些都不该算实质性变更:
- 初始化时用 structuredClone 或深比较库(如 lodash/isEqual)保存原始数据快照
- 监听表单变化时,用深比较判断当前值与初始值是否真正不同,再设置 isDirty.value = true
- 提交成功、重置表单、或调用 cancel 编辑后,务必同步重置 isDirty 和 pendingSave 状态
弹窗逻辑要稳,不能闪、不能漏、不能卡
原生 window.confirm 在某些场景下体验差,而 UI 框架弹窗(如 Element Plus 的 $confirm)是异步的,必须手动衔接 next:
- 不要直接 return $confirm(...).then(...) —— 守卫函数已执行完毕,next 不会被等待
- 正确做法:调用 next(false) 阻止默认导航 → 显示自定义弹窗 → 用户点击“确定”后再调用 next(),“取消”则不操作(或再次 next(false))
- 为防多次触发(比如快速连点返回),可在弹窗打开时设标志位,重复调用直接忽略
别忘了 beforeunload:关页/刷新场景的兜底方案
onBeforeRouteLeave 对关闭标签页、按 F5 刷新、地址栏输新网址完全无效。这时必须靠 beforeunload:
- 当 isDirty.value 变为 true 时,给 window 添加 beforeunload 监听器,返回任意字符串触发浏览器统一提示
- 一旦用户保存成功或主动放弃编辑,立即移除该监听,防止误提示和内存泄漏
- 注意:beforeunload 无法自定义文案样式,也不能阻止页面卸载,只是最后的友好提醒
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











