vue 3 中需用 watch 监听表单是否“已修改未保存”,结合 beforeunload 事件和路由守卫(如 onbeforerouteleave)拦截用户离开行为;初始快照须用 structuredclone 或 json.stringify 获取,比对前需归一化日期、数组等特殊类型,并在表单回填后立即更新快照。

Vue 3 中不能直接用 watch 监听“用户离开页面”这个动作,因为离开行为(如刷新、关闭标签页、输入新 URL)本身不是响应式数据变化,watch 无法捕获。真正可行的方式是:用 watch 监听表单是否“已修改未保存”,再结合 beforeunload 事件和路由守卫,在用户试图离开时触发弹窗提示。
用 watch 判断表单是否被修改
核心是比对当前值与初始快照,而非监听离开动作:
- 页面加载后立刻用
structuredClone(form)或JSON.stringify(form)保存初始快照,避免引用赋值 - 用
watch(form, () => { isDirty.value = !isEqual(form, initialForm) }, { deep: true })深度监听整个表单对象 - 若不用 lodash,需手动标准化特殊类型:日期转
.toISOString()、数组排序后再比对、忽略只读字段和 UI 状态(如loading) - 提交成功后,同步更新
initialForm并设isDirty.value = false
监听浏览器原生离开行为(刷新/关页)
靠 beforeunload 事件实现全场景覆盖:
- 在
onMounted中添加监听:window.addEventListener('beforeunload', handleBeforeUnload) -
handleBeforeUnload中检查if (isDirty.value) { event.preventDefault(); event.returnValue = '' } - 注意:现代浏览器会屏蔽自定义提示文字,只显示统一提示语;该事件不能异步或弹出自定义 UI
- 在
onUnmounted中移除监听,防止内存泄漏
拦截前端路由跳转(跳转/后退)
路由守卫可配合自定义弹窗,体验更可控:
- 组件内使用
onBeforeRouteLeave((to, from, next) => { ... }) - 当
isDirty.value === true时,调用next(false)阻止跳转,再弹出自定义确认框(如 Quasar Dialog 或 ElMessage) - 用户点“保存”→ 执行提交 → 成功后调用
next();点“放弃”→ 手动重置表单 +isDirty = false→ 再next() - 不建议用
window.confirm,移动端不支持且样式不可控
关键细节别漏掉
真实项目中容易出问题的点:
- 表单回填(如从 API 加载数据)后必须立即取快照,不能等
nextTick或 DOM 渲染完成,否则初始值不准 - 日期选择器、富文本编辑器常生成新引用(如新
Date实例),需在比对前做归一化处理 - 提供“还原初始值”按钮,一键执行
Object.assign(form, initialForm)并设isDirty = false - SPA 页面频繁切换时,确保
watch和beforeunload的清理逻辑完整,避免重复绑定
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










