用 Set 记录已修改字段,初始化时建立原始值快照;为各字段绑定变更事件,比对后存入 Set;提交时仅发送 Set 中字段,支持重置、UI 提示及数组/对象等特殊场景处理。

用 Set 记录已修改字段,是轻量、高效且天然去重的方案。关键不在“怎么存”,而在“何时存”和“如何比对原始值”。
初始化时建立原始值快照
表单渲染完成(或数据加载后),遍历所有可编辑字段,将字段名作为 key,当前值作为 value 存入一个普通对象(即原始状态快照)。这个对象不参与响应式,只作比对基准。
- 对 input、textarea、select 等原生控件,取
value;对 checkbox/radio,取checked状态 +name或组合标识 - 对自定义组件(如日期选择器、多选标签),需约定其提供
getRawValue()或类似接口返回可比对的原始数据(如时间戳、字符串数组) - 避免直接监听整个表单对象——深层嵌套字段修改时,diff 成本高且易漏判
绑定变更事件,动态更新 Set
为每个字段绑定 input、change 或自定义事件(如组件 emit 的 update:modelValue),在回调中做三件事:获取当前值 → 与原始快照比对 → 不同则加入 Set。
- 比对推荐用
JSON.stringify()(适用于简单结构)或浅比较(适用于基础类型+扁平对象);避免深比较影响性能 - Set 实例建议挂载在表单组件实例上(如
this.touchedFields = new Set()),方便后续读取和重置 - 注意防抖:高频输入(如搜索框)可加 300ms 防抖,但需确保最后一次输入一定触发比对
提交前校验与差异化处理
提交时,不再遍历全部字段,而是直接遍历 Set 中的字段名,仅收集这些字段的新值,构造最小化 payload。
- 例如后端 API 只接受 PATCH 请求,且要求只传被修改字段,则直接用
Array.from(this.touchedFields).reduce(...)构建数据 - 支持“取消修改”:点击重置时,清空 Set 并将表单 DOM 值恢复为原始快照(而非调用 reset(),避免丢失用户临时输入)
- UI 提示优化:用
field in this.touchedFields控制某字段旁的「已修改」小标,比监听整个表单更精准
处理特殊情况:数组/对象字段与联动更新
当字段值是数组或对象(如地址对象、标签列表),需将其“扁平化标识”后存入 Set,否则每次引用变化都会误判为修改。
- 例如:
user.profile.tags改动,存入 Set 的是"user.profile.tags"字符串,而非整个数组引用 - 联动场景(如省市区三级联动):下游字段变更时,主动将上游字段也加入 Set(如选了城市,自动标记“省份”为已修改),保证业务语义完整
- 删除操作特殊处理:如移除某条子项,原始快照中该子项存在,当前值中已不存在 → 视为修改,应记录父字段路径










