017.Diffing算法

138 阅读4分钟

一.验证Diffing算法存在

我们创建一个定时器来维护state中的date,模拟每秒时间变化,同时放置一个输入框。这样状态是一直更新的,输入框是固定的DOM元素,我们在输入框输入内容,如果状态更新时间变化,而输入框内的内容没有重置为空,说明有算法参与,识别input节点没有变化,则没有触发更新。

image.png

class Time extends React.Component{
    state={
        date:new Date()
    }

    componentDidMount(){
        setInterval(()=>{
            this.setState({date : new Date()})
        },1000)
    }

    render(){
        return(
            <div>
                <h1>Hello</h1>
                <input type="text" />
                <h2>现在是北京时间:{this.state.date.toTimeString()}
                    <input type="text" />
                </h2>
                时间每隔一秒刷新一次 而h1和input标签不变化不重新渲染 所以在页面input中输入内容不被清空
            </div>
        )
    }、
}
ReactDOM.render(<Time/>,document.getElementById('test'))

结果很容易预测,input框内容确实不会被清空。
而代码中跟时间处于同一标签内的 ‘现在是北京时间:’也会跟着状态变化而更新
但h2标签中嵌套的input输入内容,依然不会更新
所以Diffing算法会逐层对比,最小粒度是标签(节点)

二.虚拟DOM中key的作用

举个栗子:

class Person extends React.Component{
    state={
        people:[
            {id:'1',name:'老秦',age:'22'},
            {id:'2',name:'小唐',age:'38'},
            {id:'3',name:'阿香',age:'27'},
        ]
    }

    componentDidMount(){
        setInterval(()=>{
            this.setState({date : new Date()})
        },1000)
    }
    add = () =>{
        const { people } = this.state
        const p = { id:people.length+1,name:'坤泰',age:39}
        this.setState({people:[p , ...people]})
    }

    render(){
        return(
            <div>
                <h2>展示人员信息</h2>
                <button onClick={this.add}>添加人员</button>
                <span>索引值作为key</span>
                <ul>
                    {
                        this.state.people.map((item,index)=>{
                            return(
                                <li key={index}>{item.name}----{item.age}<input type="text"/></li>
                            )
                        })
                    }    
                </ul>
                <span>唯一标识作为key</span>
                <ul>
                    {
                        this.state.people.map((item)=>{
                            return(
                                <li key={item.id}>{item.name}----{item.age}<input type="text"/></li>
                            )
                        })
                    }    
                </ul>
            </div>
        )
    }
}
ReactDOM.render(<Person/>,document.getElementById('test'))

代码中对一组数据分别以索引值index作为key和唯一标识id作为key遍历生成具体DOM元素。
点击按钮在原数组前插入一条新的人员信息时,
索引值作为key:
创建虚拟DOM时,新插入的数据索引值index为0,原来的数据索引值依次加1,以此来创建虚拟DOM。 注意注意了兄弟萌,这时候Diffing算法过来跟这几条数据说把你们的key拿出来吧我瞅瞅,一看好家伙虚拟DOM的key跟原来真实DOM的key没一个一样的,你琢磨琢磨key都不一样了,是不得把这些数据全得重新渲染成真实DOM。本来只要渲染新插入的一条,就因为你的蹩脚行为,导致现在三条都要重新渲染。
如果在你渲染数据时,顺手依据索引值渲染一个输入框输入各自对应的姓氏,index一旦变了全改姓了车祸现场
唯一标识作为key:
这就不用说了,key为唯一值不会变化非常稳健,渲染时比对直接渲染新增的key对应的DOM元素即可。

image.png

总结:

 1.虚拟DOM中key的作用:
    1).简单的说: key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用
    2).详细的说:当状态中的数据发生变化时,react会根据【新数据】生成新的虚拟DOM
                随后React进行了【新虚拟DOM】和【旧虚拟DOM】的比较,比较规则如下:
        a:旧虚拟DOM找到了与新虚拟DOM相同的key:
            (1).若虚拟DOM中内容没变,直接使用之前的真实DOM
            (2).若虚拟DOM中内容变了,则生成新的真实DOM,随后替换页面中之前的真实DOM
        b:旧虚拟DOM中未找到与新虚拟DOM相同的key
            根据数据创建新的真实DOM随后渲染到页面

2.用index作为key可能会引发的问题:
    1).若对数据进行:逆序添加、逆序删除等破坏顺序的操作:
        会产生没有必要的真实DOM更新 ===>界面效果没问题 但效率低

    2).如果结构中还包含输入类的DOM
        会产生错误的DOM更新 ===>界面有问题

    3).注意:如果不存在对数据的逆序添加 逆序删除等破坏顺序的操作,仅用于渲染列表展示,
        使用index作为key是没有问题的。

3.开发中如何选择key
    1).最好每条数据的唯一标识作为key,比如id 
    2).如果只是简单的展示数据,index也可以