“就地复用”是虚拟DOM diff中类型相同时复用节点的性能优化策略,但会导致input值错乱或丢失,因其不自动同步value、光标等原生状态;需用唯一key强制重建,并配合受控组件逻辑确保值实时同步。

“就地复用”是虚拟 DOM diff 过程中的一种默认优化策略:当新旧 vnode 类型相同(比如同为 <input>、同为同一个组件),框架会直接复用已有的真实 DOM 节点或组件实例,而不是销毁重建。它提升了渲染性能,但也是表单输入框值“看似没更新”或“状态意外保留”的核心原因。
为什么输入框的值会丢失或错乱?
根本在于:复用 DOM 节点时,浏览器原生的 value、光标位置、选中范围等内部状态不会自动同步到新数据。框架只负责 patch 属性和事件绑定,不接管这些临时 DOM 状态。
- 列表排序后,
v-for没设key,Vue 把原第 0 位的 input 复用到新第 2 位——用户输的内容还在,但绑定的数据已是另一条记录 - 切换 tab 或条件渲染表单区域时,未改变
key,旧 input 节点被复用,value 没清空,placeholder 却变了,视觉与逻辑脱节 - 使用
v-model但数据源异步更新延迟,input 已聚焦,框架还没来得及同步 value,造成短暂“输入消失”
关键解法:用 key 强制触发重建
给表单容器或具体 input 加上业务语义明确的 key,是中断就地复用最直接有效的方式。
- 对整个表单块:
:key="currentFormId"或:key="route.name + JSON.stringify(params)" - 对列表中的每个 input:
:key="item.id + '-name-input'"(不用index) - 重置表单时:
:key="resetKey",点击“重置”后执行resetKey++
补充手段:主动同步与防御性处理
即使复用了节点,也要确保输入值始终反映最新数据。
- 避免只在
mounted或useEffect初始化一次 value;改用:value="form.name"+@input双向驱动 - 对受控组件,确保 props 更新时显式调用
input.value = newValue(配合nextTick或useEffect依赖数组) - 调试时观察控制台是否只打印一次
mounted或useEffect执行日志——若没触发销毁钩子,大概率被复用了
就地复用不是缺陷,而是性能与可控性的权衡。识别它、用好 key、再辅以受控逻辑,表单状态就能既稳定又高效。









