在 React 中,key 是一个特殊的字符串属性,用于帮助 React 识别哪些元素发生了变化、被添加或被删除,从而在渲染列表时高效地更新 DOM。
核心作用:标识列表中元素的唯一性
当 React 渲染一个列表(如 map 生成的 JSX 元素数组)时,它需要一种机制来追踪每个元素的身份。key 就是这个身份标识。
// ✅ 推荐:使用唯一 ID 作为 key
const todoList = todos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
));
如果没有 key,React 只能根据元素的索引位置来判断变化,这会导致性能问题和状态错乱。
为什么需要 key?(对比有无 key 的区别)
情景:在列表开头插入一个新项
假设有一个列表:['Alice', 'Bob']
现在在开头插入 'Charlie',变为:['Charlie', 'Alice', 'Bob']
- 没有
key(或使用索引作为 key):- React 认为:
- 索引 0:从
'Alice'变为'Charlie'→ 更新元素 - 索引 1:从
'Bob'变为'Alice'→ 更新元素 - 索引 2:新增
'Bob'→ 创建元素
- 索引 0:从
- 结果:React 会重新渲染所有三个元素,效率低下。
- React 认为:
- 有稳定
key(如user.id):- React 认为:
'Charlie'是新元素 → 创建'Alice'(key=’alice’) 和'Bob'(key=’bob’) 未变 → 复用
- 结果:React 只创建新元素,复用原有元素,性能更优。
- React 认为:
key 的选择原则
✅ 推荐:使用稳定、唯一、不变的 ID
// 最佳实践:使用数据本身的唯一 ID
const users = usersList.map((user) => (
<UserCard key={user.id} user={user} />
));
⚠️ 不推荐:使用数组索引(index)
// ❌ 仅在以下情况可接受:
// - 列表是静态的(不排序、不筛选、不增删)
// - 列表项没有内部状态或依赖顺序
const items = list.map((item, index) => (
<div key={index}>{item.name}</div>
));
使用索引作为 key 的问题:
- 当列表重新排序或在中间插入/删除项时,索引会变化,导致 React 错误地复用组件实例。
- 如果列表项内部有状态(如输入框),状态会错乱(输入框的值会“跳”到其他项上)。
key 的工作原理
- Diff 算法优化:React 在 reconciliation(协调)过程中,会比较新旧元素的
key。 - 复用或销毁:如果
key相同,React 认为是同一个元素,尝试复用并更新;如果key不同,则创建新元素或销毁旧元素。 - 不影响 props:
key不会作为props传递给组件,它仅在 React 内部使用。
function ListItem({ children }) {
// key 不在 props 中
console.log(props.key); // undefined
return <li>{children}</li>;
}
key 应该放在哪里?
key 应该赋值给数组中直接返回的 JSX 元素,而不是该元素内部的子元素。
// ✅ 正确:key 在 <li> 上
const listItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
// ❌ 错误:key 在 <div> 内部,无效
const listItems = todos.map((todo) =>
<li>
<div key={todo.id}>{todo.text}</div>
</li>
);
总结
| 要点 | 说明 |
|---|---|
| 作用 | 帮助 React 识别列表元素的身份,优化渲染性能。 |
| 推荐值 | 使用数据中稳定、唯一、不变的 ID(如数据库 ID)。 |
| 避免使用 | 数组索引(除非列表完全静态)。 |
| 位置 | 放在 map 返回的最外层 JSX 元素上。 |
| 特殊性 | 仅 React 内部使用,不会传递给组件 props。 |
核心思想:key 是 React 高效更新列表的“身份证”。使用正确的 key 可以避免不必要的重新渲染,防止状态错乱,是编写高性能 React 应用的关键实践。
THE END


