输入框失焦的本质是虚拟DOM diff中input节点被判定为需替换而非复用,导致原生元素重建;关键在于确保key唯一稳定、属性引用不变、慎用v-if,并利用就地复用机制保护节点。

输入框失焦的本质原因
虚拟 DOM 的 diff 和 patch 过程中,若新旧 vnode 对应的 input 元素被判定为“需要替换”而非“复用”,Vue 或 React 就会销毁旧节点、插入新节点。即便 DOM 结构看似一致,只要 key 缺失、type 改变、ref 变化、或 props(如 v-model 绑定值触发重新渲染)导致 vnode 不稳定,浏览器原生 input 元素就会被重建 —— 焦点自然丢失。
确保 input 节点稳定复用的关键做法
核心是让 diff 算法识别出:这是同一个 DOM 元素,不该动它。
-
显式设置唯一且稳定的 key:尤其在列表中渲染 input 时,避免用 index 作 key;改用每项数据的唯一 ID(如
item.id),防止排序/过滤后 key 错位引发误替换 -
避免无必要地改变 input 的绑定属性:例如不要在每次 render 中新建对象传给
v-bind,或把函数内联写在模板里导致每次生成新引用;v-model 的 value 和事件处理器应保持引用稳定 -
慎用 v-if 切换 input:相比
v-show,v-if会彻底卸载组件,必然失焦;若需条件显示,优先用v-show或用 CSS 控制 visibility -
自定义指令手动聚焦(兜底方案):当逻辑复杂难以完全避免 patch 时,可用
v-focus指令,在mounted和updated钩子中判断是否需要调用el.focus(),但注意避免重复聚焦
Patch 过程中的节点保护机制
以 Vue 为例,patch 时会对具有相同 key 和 type(即同为 input)的元素尝试“就地复用”。此时 Vue 会保留原 DOM 节点,仅更新其属性(如 value、checked)和事件监听器。这种保护的前提是:
- vnode 的
key存在且相等 - tag 名一致(
input≠textarea) - 静态属性(如
type、name)未发生不可兼容变更(比如从text改成hidden会强制替换) - 没有跨组件边界替换(比如父组件重渲染导致子组件整个 vnode 树被新生成)
调试与验证方法
快速确认是否是 patch 导致失焦:
- 在开发者工具中勾选 “Preserve log”,输入时观察 console 是否出现
input节点被removeChild/appendChild - 给 input 添加
ref="myInput",在updated钩子中打印this.$refs.myInput === this.prevRef,看是否指向同一 DOM 实例 - 使用 Vue Devtools 查看对应组件的 vnode,对比更新前后 key、tag、props 是否稳定










