直接回答:key 是 vnode 的身份标识:diff 时框架按 key 匹配新旧列表的节点,匹配的复用 DOM 并更新属性,不匹配的新建或删除。没有 key(或 key 不稳定)框架只能按位置对齐——就地更新每个位置的 DOM。用 index 当 key 在“纯追加”场景无害,但在插入、删除、重排场景引发两类问题:性能(错位复用导致全列表属性重写)与正确性(组件内部状态错配到别的行)。
展开解析:正确性问题是重灾区且隐蔽:列表项里有受控外的内部状态(input 未受控的输入值、展开/收起状态、动画进行中、聚焦态),index 作 key 时删除中间一行,后面的 DOM 被复用给“新位置”的数据,内部状态跟着 DOM 走——用户看到的是 B 行显示着 C 的输入内容。性能层面:头部插入一项,index 对齐导致所有位置内容变化,框架逐行 patch 文本与属性,N 次更新;稳定 key 则识别出整体位移,只新建一个节点加移动 DOM(React 对移动仍需逐对比较,Vue 的最长递增子序列算法把移动次数压到最小)。key 的选择纪律:用数据的唯一稳定 ID(数据库主键、内容哈希);没有 ID 的数据源(如纯字符串列表)追加场景可妥协 index 但文档注明;绝不用 Math.random()(每次渲染全新身份等于禁用复用,比 index 更糟)。跨框架一致性:React、Vue、Svelte 语义相同——这不是某框架的实现细节而是“身份驱动复用”的通用模型。面试延伸:key 变化会强制组件销毁重建(状态重置),这个特性可用作“重置子组件”的合法技巧()。
追问方向:Vue 的就地复用策略与 React 默认行为差异?key 与 TransitionGroup 动画的关系?
(约 490 字)