React 键的意义——视觉解释

2025-06-09

React 键的意义——视觉解释

免责声明:这是一个过于简化的说法,应该作为和解如何运作的基本指南,而不是一个完美的例子!

在 React 中处理数组时,每个元素的“key”属性的使用对于避免不必要的重新渲染性能损失至关重要。本文将解释为什么你应该始终清晰地定义键,以及如果不这样做会错过什么。

让我们从一个数组开始

const joshs = [{  Name: "Josh", }, { Name: "Joshina", }, {  Name: "Notjosh", }]
Enter fullscreen mode Exit fullscreen mode

React 组件的业务端,用于渲染上述数组

<div>
    { joshs.map((person, index) => ( <span key={index}>{person.name}</span>)) }
</div>
Enter fullscreen mode Exit fullscreen mode

以及它输出的 HTML

<div>
    <span key=’0’>Josh</span>
    <span key=’1’>Joshina</span>
    <span key=’3’>Notjosh</span>
</div>
Enter fullscreen mode Exit fullscreen mode

效果很好!

...但!

新的乔希已经到来,他告诉其他人让开。

 [{ Name: "Mega Josh"}, {  Name: "Josh", }, { Name: "Joshina", }, {  Name: "Notjosh", }]
Enter fullscreen mode Exit fullscreen mode

我们的组件接收新列表,执行其操作......

<div>
    { joshs.map((person, index) => ( <span key={index}>{person.name}</span>)) }
</div>
Enter fullscreen mode Exit fullscreen mode

并像以前一样将其打印出来。

<div>
    <span key=’0’>Mega Josh</span>
    <span key=’1’>Josh</span>
    <span key=’2’>Joshina</span>
    <span key=’3’>Notjosh</span>
</div>
Enter fullscreen mode Exit fullscreen mode

效果很好!

...但!

让我们以非常简单的方式,深入了解一下 React 在呈现其新列表时所经历的过程。

React 组件,当你将其简化为最原始的形式(即从 JSX 转换而来)时,只是一个带有一组属性的对象。这些属性定义了它的类型、名称、状态、它接收的 props、是否有子组件等等。

每次数组发生变化时,<span>都会创建一个新的 Josh 组件对象列表。React协调器会将新创建的对象与 DOM 中的当前版本进行比较。如果检测到某些属性之间存在任何差异,它会重新绘制组件,并认为它们是同一个对象,只是属性发生了变化。

因此,在我们的示例中,我们有原始数组(组件),可以大致翻译成如下内容......

[{
  Type: "span",
  Key: "0",
  Children: "Josh"
}, {
  Type: "span",
  Key: "1",
  Children: "Joshina"
}, {
  Type: "span",
  Key: "2",
  Children: "Notjosh"
}]
Enter fullscreen mode Exit fullscreen mode

协调器将查看键和组件属性(在我们的简化情况下为内容或子项),然后查看其先前的组件列表,以查看它是否与任何先前的组合匹配。

由于我们的列表使用数组索引位置作为其键,当“Mega Josh”到达并将所有组件向下移动一个位置时,由于 React 注意到键与它们之前的属性不匹配,每个比较现在都会失败!

[{
  Type: "span",
  Key: "0",                // Expected 0 to match 'Josh'
  Children: "Mega Josh"     // IM DIFFERENT, REDRAW ME
}, {
  Type: "span",
  Key: "1",                // Expected 1 to match 'Joshina'
  Children: "Josh"          // IM DIFFERENT, REDRAW ME
}, {
  Type: "span",
  Key: "2",                // Expected 2 to match 'Notjosh'
  Children: "Joshina"       // IM DIFFERENT, REDRAW ME
}, {
  Type: "span",   
  Key: "3",                // IM NEW
  Children: "Notjosh"       // DRAW ME
}]
Enter fullscreen mode Exit fullscreen mode

但是!我们可以避免这种情况。如果我们明确定义一个静态的、唯一的、并且与其关联的属性唯一关联的键,那么即使它的位置发生了变化,React 也可以识别出它是同一个组件。

让我们用唯一的键重建我们的组件

注意:在本例中,我使用name属性来保持 josh 对象的简洁,但这并非最佳实践,因为两个组件拥有相同键的可能性相当高。尽可能使用数据对象中的某种主键。

<div>
    { people.map((person, index) => ( <span key={`key-${person.name}`}>{person.name}</span>)) }
</div>
Enter fullscreen mode Exit fullscreen mode

导出的 HTML 现在看起来像

<div>
    <span key=’key-Josh’>Josh</span>
    <span key=’key-Joshina’>Joshina</span>
    <span key=’key-Notjosh’>Notjosh</span>
</div>
Enter fullscreen mode Exit fullscreen mode

以及更新后的数组 HTML

<div>
    <span key='key-Mega Josh'>Josh</span>
    <span key=’key-Josh’>Josh</span>
    <span key=’key-Joshina’>Joshina</span>
    <span key=’key-Notjosh’>Notjosh</span>
</div>
Enter fullscreen mode Exit fullscreen mode

现在,键对于它们的数据对象(而不是它们的数组位置)来说是唯一的,因此当我们进行对象比较时

[{
  Type: "span",
  Key: "key-Josh",
  Children: "Josh"
}, {
  Type: "span",
  Key: "key-Joshina",
  Children: "Joshina"
}, {
  Type: "span",
  Key: "key-Notjosh",
  Children: "Notjosh"
}]
Enter fullscreen mode Exit fullscreen mode

协调器会发现有些组件并没有改变,只是移动了位置。新的组件会被创建并插入到列表的最前面,而不会影响到后面的组件。太棒了!

[{
  Type: "span",
  Key: "key-Mega Josh",    // IM NEW
  Children: "Mega Josh"     // DRAW ME
}, {
  Type: "span",
  Key: "key-Josh",         // Expected 'key-Josh' to match 'Josh'
  Children: "Josh"          // IM THE SAME, DONT REDRAW ME
}, {
  Type: "span",
  Key: "key-Joshina",      // Expected 'key-Joshina' to match 'Joshina'
  Children: "Joshina"       // IM THE SAME, DONT REDRAW ME
}, {
  Type: "span",
  Key: "key-Notjosh",      // Expected 'key-Notjosh' to match 'Notjosh'
  Children: "Notjosh"       // IM THE SAME, DONT REDRAW ME
}]
Enter fullscreen mode Exit fullscreen mode

对于某些用途,性能影响可能很小(如果数组顺序不变,甚至可能不存在)。添加键的好处在经过排序/重新排序的大型数组中尤为明显,因为它可以消除大部分列表的渲染需求。真是太神奇了!

鏂囩珷鏉ユ簮锛�https://dev.to/jtonzing/the-significance-of-react-keys---a-visual-explanation--56l7
PREV
使用 React Query 解决 React 应用中的状态管理
NEXT
构建一个 React 应用来解决所有数独难题。AWS 安全直播!