React 中 key 的作用是什么?为什么不能用 index 做 key?
key 的作用
key 是 React Diff 算法识别列表项的唯一标识,帮助 React 判断哪些元素变化了。
Diff 过程
React 将新旧虚拟 DOM 对比:
- 相同 key 的元素视为同一项,复用 DOM 节点
- 不同 key 的元素视为不同项,销毁重建
- key 不变但位置变化,只移动 DOM 而不重建
为什么不能用 index 做 key
场景:列表头部插入新元素
// 初始列表
// index 0: 'Apple'
// index 1: 'Banana'
// index 2: 'Cherry'
// 头部插入 'Orange'
// index 0: 'Orange' ← key 没变但内容变了
// index 1: 'Apple' ← key 没变但内容变了
// index 2: 'Banana' ← key 没变但内容变了
// index 3: 'Cherry' ← 新增React 认为前 3 个 key 没变,会复用它们的 DOM 节点并更新内容。所有元素都需要重新渲染,而不是只渲染新元素。
正确做法
// ✅ 用唯一 ID 做 key
{items.map(item => <Item key={item.id} data={item} />)}index 做 key 导致的 bug
1. 输入框错位
// 列表项包含输入框,头部插入新项时,输入框内容会跟着错位
{items.map((item, index) => (
<div key={index}>
<input defaultValue={item.value} />
</div>
))}
// 第一个输入框的内容会变成第二个的2. 动画异常
// CSS transition 基于 DOM 元素身份,index 做 key 会导致动画绑定到错误元素3. 组件状态混乱
// 有状态的子组件,状态会绑定到错误的列表项
{items.map((item, index) => (
<ExpandableItem key={index} data={item} /> // 展开状态会错位
))}什么时候可以用 index
- 列表纯展示,无增删排序操作
- 列表项是无状态组件
- 列表项不需要动画
// 静态列表可以用 index
{staticOptions.map((opt, i) => <option key={i}>{opt}</option>)}总结
- key 应该稳定、唯一、可预测
- 用业务 ID 做 key
- 避免 index 做 key(除非列表完全静态)