[React] DOM的diffing算法

目录

· 虚拟DOM中key的作用

· 使用遍历时的index作为key会引发的问题

· 开发中如何选择key值


首先,我们知道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,并渲染在页面上,此情况效率较高,且会解决其他界面错乱问题。

猜你喜欢

转载自blog.csdn.net/weixin_44384728/article/details/128684778