最近在看一本名叫《深入浅出React和Redux》这一书,里面谈到了react的dom更新比对,记录一下。
假设有这么一个组件
<ul>
<ListItem text="first" />
<ListItem text="second" />
<ListItem text="third" />
</ul>
现在,我们在这个组件的前面插入一个新的组件<ListItem text='zero' />
<ul>
<ListItem text="zero" />
<ListItem text="first" />
<ListItem text="second" />
</ul>
思考,怎么更新dom是最优的,react是去怎么更新?
按照我们的思维,最优的更新dom就是去把新增一个
ListItem
组件,放在第一个。把之前的第一个组件<ListItem text="first" />
以及第二个组件<ListItem text="second" />
往后挪一位。这样的结果是最好的。
可是react不是这样更新的!
它先去比较第一个
ListItem
组件,发现组件上的text
值first
变成了zero
,需要更新。第二个组件text
之前的second
变成了first
,也需要更新,最后新创建一个组件,把它的text
设置为second
。react就完成了它的更新。
当然, React 并不是没有意识到这个问题,所以 React 提供了方法来克服这种浪费,不过需要开发人员在写代码的时候提供一点小小的帮助,这就是 key 的作用。
key的用法
在 React 的眼里,确定每一个组件在组件序列中的唯一标识就是它的位置,所以
它也完全不懂哪些子组件实际上并没有改变,为了让 React 更加“聪明”,就需要开发者
提供一点帮助。
如果在代码中明确地告诉 React 每个组件的唯一标识,就可以帮助 React 在处理这个
问题时聪明很多,告诉 React 每个组件“身份证号”的途径就是 key 属性。
- 把之前的代码加上key
<ul>
<ListItem key={1} text="first" />
<ListItem key={2} text="second" />
<ListItem key={3} text="third" />
</ul>
现在再去插入一个ListItem
组件放在最前面,让它key为0
<ul>
<ListItem key={0} text="zero" />
<ListItem key={1} text="first" />
<ListItem key={2} text="second" />
</ul>
现在,react根据key值,知道了第二个第三个组件是之前的第一个第二个,所以react会创建一个ListItem
组件放在第一位,对于原有的两个组件只用原有的props触发更新。这里就需要组件内部的shouldComponentUpdate
的钩子函数进行判断来减少不必要的更新。
但是这个 key 值只是唯一还不足够,这个 key 值还需要是稳定不变的,试想,如果
key 值虽然能够在每个时刻都唯一,但是变来变去,那么就会误导 React 做出错误判断,
甚至导致错误的渲染结果。
<ul>
{
Arr.map((item,index)=>(<ListItem key={index} text={item.text} />))
}
</ul>
用数组下标作为 key ,看起来 key 值是唯一的,但是却不是稳定不变的,随着 Arr
数组值的不同,同样一个Listltem
实例在不同的更新过程中在数组中的下标完全可能不
同,把下标当做 key 就让 React 彻底乱套了。
需要注意,虽然 key 是一个 prop ,但是接受 key 的组件并不能读取到 key 的值,因为key
ref
React 保留的两个特殊 prop ,并没有预期让组件直接访问。