React key 的理解

972 阅读4分钟

js

写react的时候经常会接触到key。那么这个key到底有什么作用呢?我们一起来看一下。

我们知道react有虚拟DOM和diff算法来优化react的性能。通过它们react会去比较渲染前和渲染后的DOM树来判断要不要重新渲染或者说怎么重新渲染。如果某个元素的子元素是动态数组类型的,我们就要加上key,来保证子元素的唯一性。

我们来看一个栗子:

//old tree
<ul>
  <li>first</li>
  <li>second</li>
</ul>

//new tree
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

当React同时迭代比较前后两棵元素树的子元素列表时,性能相对不会太差,因为前两个项都是相同的,新的元素树中有第三个项目,那么React会比较first树与second树之后,插入third树,但是下面这个栗子就不同了

//old tree
<ul>
  <li>first</li>
  <li>second</li>
</ul>

//new tree
<ul>
  <li>third</li>
  <li>first</li>
  <li>second</li>
</ul>

React在比较第一个元素就发现了差异(first与third),如果React将第一个元素中的内容进行更新,那么你会发现第二个元素(second与first)也需要将元素中内容进行更新,并且第三个元素需要插入一个新的元素second,但事实真的是如此吗?其实不然,我们发现新的元素树和旧的元素树,只有第一项是不同的,后两项其实并没有发生改变,如果React懂得在旧的元素树开始出插入third,那么性能会极大的提高,关键问题是React如何进行这种判别,这时React就用到了key属性。例如:

//old tree
<ul>
  <li key="first">first</li>
  <li key="second">second</li>
</ul>

//new tree
<ul>
  <li key="third">third</li>
  <li key="first">first</li>
  <li key="second">second</li>
</ul>

通过key值React比较<li key="first">first与<li key="third">third时,会发现key值是不同,表示<li key="third">third是新插入的项,因此会在开始处插入<li key="third">third,随后分别比较<li key="first">first与<li key="second">second,发现li项没有发生改变,仅仅只是被移动而已。这种情况下,性能的提升是非常可观的。因此,从上面看key值必须要稳定、可预测的并且是唯一的。不稳定的key(类似于Math.random()函数的结果)可能会产生非常多的组件实例并且DOM节点也会非必要性的重新创建。这将会造成极大的性能损失和组件内state的丢失。

到这里我们已经基本明白了key属性在React中的作用,因为key是React内部使用的属性,所以在组件内部是无法获取到key值的,如果你真的需要这个值,就需要换个名字再传一次了。

其实还有一个现象不知道大家观察到了没有,我们来看一下:

//case1
function App() {
  return (
    <ul>
      {
        [
          <li key={1}>1</li>,
          <li key={2}>2</li>
          ]
        }
    </ul>
  )
}
//case2
function App() {
  return (
    <ul>
      <li>1</li>
      <li>2</li>
    </ul>
  )
}

我们会发现,第一种场景是需要传入key值的,第二种就不需要传入key,为什么呢?其实我们可以看一下JSX编译之后的代码:

//case1
function App() {
  return React.createElement('ul',null,[
    React.createElement('li',{key: 1}, "1"),
    React.createElement('li',{key: 2}, "2")
  ])
}
//case2
function App() {
  return React.createElement('ul',
    null,
    React.createElement('li',{key: 1}, "1"),
    React.createElement('li',{key: 2}, "2")
  )
}

我们发现第一个场景中,子元素的传入以数组的形式传入第三个参数,但是在第二个场景中,子元素是以参数的形式依次传入的。在第二种场景中,每个元素出现在固定的参数位置上,React就是通过这个位置作为天然的key值去判别的,所以你就不用传入key值的,但是第一种场景下,以数组的类型将全部子元素传入,React就不能通过参数位置的方法去判别,所以就必须你手动地方式去传入key值。

好了,关于 react key 的理解就介绍到这里啦,欢迎交流哈。