beforeRouteLeave用于拦截路由离开,需手动判断isDirty状态并调用next(false)中断后弹窗确认,Vue 3中必须在setup顶层用onBeforeRouteLeave注册。

在组件内用 beforeRouteLeave 拦截离开操作,核心是“判断是否该拦 + 拦了之后怎么让用户选”。它不是自动弹窗,而是给你一个机会做逻辑控制,尤其适合表单未保存场景。
必须写在组件定义里,不能放异步或条件分支中
Vue 3(Composition API)必须用 onBeforeRouteLeave,且要在 setup() 顶层调用:
- ✅ 正确:直接在
setup()开头写onBeforeRouteLeave((to, from, next) => { ... }) - ❌ 错误:放在
watch回调、if判断块里,或等接口返回后再注册 —— 守卫不会生效 - Options API 更简单:直接作为组件选项属性,写
beforeRouteLeave(to, from, next) { ... }
关键要自己判断“要不要拦”,不能只靠守卫触发
守卫本身不决定拦不拦,它只是个钩子。你需要配合一个状态变量(比如 isDirty)来标记数据是否被修改:
Vue 3 组合式 API 与 最佳实践,涵盖代码组织、useXxx 模式、组合式函数设计、Store 集成、响应式优化及功能抽取。
- 初始化时存一份原始数据快照,例如
original = ref(JSON.stringify(form)) - 监听表单变化,一旦不同就设
isDirty.value = true(推荐用watch(form, () => ..., { deep: true })) - 提交成功或重置后,记得把
isDirty设回false
弹窗确认必须手动控制导航,不能依赖 next() 同步返回
浏览器弹窗(如 ElMessageBox 或 confirm())是异步的,但 next() 必须同步调用。所以标准做法是:
立即学习“前端免费学习笔记(深入)”;
- 先调
next(false)立即中断跳转 - 再弹窗,用户点“确定”后调
router.push(to.fullPath)或router.back() - 用户点“取消”则什么都不做,页面留在原地
完整逻辑示例(Vue 3 + setup)
假设你有一个编辑页,表单叫 form,路由实例为 router:
- 定义响应式状态:
const isDirty = ref(false),初始快照:const original = ref(JSON.stringify(form)) - 监听变更:
watch(form, () => isDirty.value = JSON.stringify(form) !== original.value, { deep: true }) - 注册守卫:
onBeforeRouteLeave((to, from, next) => { if (isDirty.value) { ElMessageBox.confirm('有未保存的修改,确定离开?').then(() => router.push(to.fullPath)).catch(() => {}); next(false); } else { next(); } })


















