
写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值。