目录
首先,我们知道React的特点之一就是:使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互。其中React的实现如下:
在数据更新后,React自动生成新的虚拟DOM,通过比较新、旧虚拟DOM,找出发生改变的虚拟DOM,将其转变为真实DOM,最终展现在页面上。
因此,在进行组件遍历的时候需要加一个key来区分不同的标签,这里需要进行上图所述的虚拟DOM的diffing对比。
· 虚拟DOM中key的作用
1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, 随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
(1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
(2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b. 旧虚拟DOM中未找到与新虚拟DOM相同的key ,根据数据创建新的真实DOM,随后渲染到到页面
具体如图所示:
· 使用遍历时的index作为key会引发的问题
我们首先写出如下Person组件,使用state保存初始数据:
class Person extends React.Component{
state = {
persons:[
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19},
]
}
add = ()=>{
const {persons} = this.state
const p = {id:persons.length+1,name:'小王',age:20}
//这里添加信息时逆序添加数据
this.setState({persons:[p,...persons]})
}
}
接下来我们使用循环遍历将state中的person信息展示在组件上,其中index作为key:
render(){
return (
<div>
<h2>展示人员信息</h2>
<button onClick={this.add}>添加一个小王</button>
<h3>使用index(索引值)作为key</h3>
<ul>
{
this.state.persons.map((personObj,index)=>{
return <li key={index}>{personObj.name}---{personObj.age}</li>
})
}
</ul>
)
}
由于点击按钮后触发add()函数,逆序添加数据,所以DOM的更新具体步骤如下:
使用index唯一标识作为key
初始数据:
[
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19}
]
初始的虚拟DOM:
<li key=0>小张---18</li>
<li key=1>小李---19</li>
更新后的数据:
[
{id:3,name:'小王',age:20},
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19}
]
更新数据后的虚拟DOM:
<li key=0>小王---20</li>
<li key=1>小张---18</li>
<li key=2>小李---19</li>
新旧虚拟DOM此时根据key值进行diff对比,发现所有标签都不同,需要重新将所有虚拟DOM转为真实DOM,渲染在页面上,虽然界面效果没问题, 但由于原有数据并未发生改变,又重新改变了所有的真实DOM。所以会产生效率低下的问题。如果结构中还包含一些输入类的DOM,例如input标签,则会产生一系列界面错乱问题。
· 开发中如何选择key值
最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值;本例中我们使用person信息中的id作为key值,代码具体如下:
render(){
return (
<div>
<h2>展示人员信息</h2>
<button onClick={this.add}>添加一个小王</button>
<h3>使用id(数据的唯一标识)作为key</h3>
<ul>
{
this.state.persons.map((personObj,index)=>{
return <li key={personObj.id}>{personObj.name}---{personObj.age}</li>
})
}
</ul>
)
}
此时DOM的更新具体步骤如下:
使用id唯一标识作为key
初始数据:
[
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19}
]
初始的虚拟DOM:
<li key=1>小张---18</li>
<li key=2>小李---19</li>
更新后的数据:
[
{id:3,name:'小王',age:20},
{id:1,name:'小张',age:18},
{id:2,name:'小李',age:19}
]
更新数据后的虚拟DOM:
<li key=3>小王---20</li>
<li key=1>小张---18</li>
<li key=2>小李---19</li>
将新旧虚拟DOM此时根据key值进行diff对比,发现只有新添加的标签不同,所以只需要将新添加的标签的虚拟DOM转化为真实DOM,并渲染在页面上,此情况效率较高,且会解决其他界面错乱问题。