应监听整个表单对象并比对初始快照来标记“已修改但未保存”:页面加载后用 structuredClone 或 JSON.stringify 保存初始值,watch 配合 deep: true 和 isEqual 判断差异,提交后更新快照;分散 ref 可数组监听,需防首次触发和引用变化误判,结合路由守卫提示用户。

用 watch 监听表单所有字段变更,核心是把表单数据源(通常是 ref 或 reactive 对象)作为监听目标,并在变化时标记“已修改但未保存”。关键不是监听每个字段,而是监听整个表单状态的“快照差异”。
监听整个表单对象(推荐)
适用于表单数据集中管理(如一个 form reactive 对象)。利用 deep: true 深度监听,配合初始快照比对:
- 页面加载后,用
JSON.stringify(form)或structuredClone(form)保存初始值(注意:避免直接赋值引用) - 用
watch(form, () => { isDirty.value = !isEqual(form, initialForm); }, { deep: true })(isEqual可用 Lodash,或简易对比逻辑) - 提交成功后,重置
isDirty并更新initialForm快照
监听多个 ref 字段(适合分散定义)
当表单字段是独立的 ref(如 usernameRef, emailRef),可用数组语法批量监听:
- 定义
const formRefs = [usernameRef, emailRef, ageRef] watch(formRefs, () => { isDirty.value = true; }, { deep: true })- 注意:首次赋值触发也会触发 watch,建议加防抖或初始化后才启用监听(如
onMounted(() => watch(...)))
避免误触发的细节处理
用户没真改内容,但对象引用变了(比如选日期后生成新 Date 实例),会导致 isDirty 错误为 true:
- 对 Date、Array、Object 类型字段,监听前先做标准化(如转成 ISO 字符串、JSON 序列化后再比对)
- 忽略只读字段、计算字段、临时 UI 状态(如 loading、showPassword)
- 提交失败或取消编辑时,提供“还原为初始值”按钮,手动同步
form和initialForm
结合路由守卫自动提示
检测到 isDirty.value === true 且用户要离开页面时,弹出确认框:
- Vue Router 中使用
beforeEach全局守卫,或组件内onBeforeRouteLeave - 调用
next(false)阻止跳转,再window.confirm('有未保存的修改,确定离开?') - 注意:现代浏览器对非用户手势触发的
confirm有限制,生产环境建议用自定义弹窗 + 手动控制路由
不复杂但容易忽略。重点在于“初始快照”的获取时机和比对方式是否稳定,而不是监听本身。

















