在 vue 3 中,watcheffect 可自动监听表单字段变化并更新 hasunsavedchanges 标志位,通过 json.stringify 快照对比实现简易脏检查,配合路由守卫或 beforeunload 实现离开提示,提交后需手动重置标志位与快照。

在 Vue 3 中,用 watchEffect 监听表单字段变化并自动设置“离开提示”标志位(比如 isDirty 或 hasUnsavedChanges),是一种简洁、响应式的方式。它比手动在每个 v-model 后写逻辑更可靠,也避免了遗漏字段。
监听所有相关表单字段
把需要触发离开提示的字段(如 form.name、form.email、form.content)在 watchEffect 回调中直接读取,Vue 会自动追踪依赖:
const form = reactive({
name: '',
email: '',
content: ''
})
const hasUnsavedChanges = ref(false)
const originalForm = JSON.stringify(form) // 快照用于对比(可选)
watchEffect(() => {
// 触发依赖收集:读取所有要监控的字段
const current = JSON.stringify(form)
hasUnsavedChanges.value = current !== originalForm
})
这样只要任意字段变化,hasUnsavedChanges 就会自动更新。注意:使用 JSON.stringify 是简单粗暴但实用的做法,适用于结构较平、不含函数/undefined/Date 等不可序列化值的表单。
更精准的变更检测(推荐用于复杂场景)
如果表单嵌套深、含引用类型或需排除某些字段,建议用 lodash.isEqual 或手写浅比较,并在初始化时保存原始副本:
- 用
toRaw或structuredClone(支持环境下)保存初始值 - 在
watchEffect内调用比较函数,避免 JSON 序列化副作用 - 对大型表单,可只监听关键字段,减少不必要的响应开销
配合路由守卫或离开确认弹窗
有了 hasUnsavedChanges,就可以自然接入 beforeRouteLeave(Vue Router)或浏览器原生 beforeunload:
// 组合式 API 路由守卫(需在 setup 中)
onBeforeRouteLeave((to, from, next) => {
if (hasUnsavedChanges.value && !confirm('有未保存的修改,确定要离开?')) {
return
}
next()
})
// 或绑定到 window(谨慎使用,仅作兜底)
watch(hasUnsavedChanges, (val) => {
if (val) {
window.addEventListener('beforeunload', beforeUnloadHandler)
} else {
window.removeEventListener('beforeunload', beforeUnloadHandler)
}
})
重置标志位的时机要明确
提交成功、取消编辑、或点击“重置”后,记得同步清空标志位:
- 调用
hasUnsavedChanges.value = false - 同时更新
originalForm快照(若用 JSON 方式) - 避免因异步提交完成滞后导致标志位残留
不复杂但容易忽略的是:watchEffect 默认是 lazy 的(首次执行立即触发),所以初始化时就会比对一次 —— 这正好符合“刚进页面没改过,标志为 false”的预期。只要字段响应式正确、依赖读取完整,整个流程就全自动运行。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










