react学习(九)Diffing算法

本文介绍React中的Diffing算法原理及应用,通过实例演示如何利用该算法提高页面渲染效率,并探讨虚拟DOM中key的重要性。

1 Diffing算法

React维护了一个虚拟DOM,在页面渲染时,React会把虚拟DOM中的每个标签和真实DOM进行比较,有变化才更新,没变化不更新,这就是React中的Diffing算法。下面我们写一个简单的示例演示Diffing。

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>hello_react</title></head>
<body>
    <div id="test1"></div>
    <!--引入react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--引入react-dom,用于支持react操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于将jsx转换为js-->
    <script type="text/javascript" src="../js/babel.js"></script>
    <!--引入prop-types,用于对组件标签属性进行限制-->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <script type="text/babel"> /*此处一定要写babel*/
        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'/>
                        <span>现在是:{this.state.date.toTimeString()}</span>
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Time/>, document.getElementById('test1'))
    </script>
</body>
</html>

我们会看到页面上的时间一直在更新,而我们在输入框中输入内容,如果输入框这个标签有更新的话,其内容必将消失,但是我们看到的是输入框中的内容一直不变,这就说明了Diffing算法确实起作用了。

2 虚拟DOM中的key

虚拟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和使用id作为key的比较

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>hello_react</title></head>
<body>
    <div id="test1"></div>
    <!--引入react核心库-->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!--引入react-dom,用于支持react操作DOM-->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!--引入babel,用于将jsx转换为js-->
    <script type="text/javascript" src="../js/babel.js"></script>
    <!--引入prop-types,用于对组件标签属性进行限制-->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <script type="text/babel"> /*此处一定要写babel*/
        /**
         慢动作回放----使用index索引值作为key
            数据:
                {id:1,name:'小张',age:18},
                {id:2,name:'小李',age:19},
            初始的虚拟DOM:
                <li key=0>小张---18</li>
                <li key=1>小李---19</li>
            更新后的数据:
                <li key=3>小王---20</li>
                <li key=0>小张---18</li>
                <li key=1>小李---19</li>
            更新树后的虚拟DOM:
                <li key=0>小王---20</li>
                <li key=1>小张---18</li>
                <li key=2>小李---19</li>

------------------------------------------------------------

         慢动作回放----使用id唯一标识索引值作为key
            数据:
                {id:1,name:'小张',age:18},
                {id:2,name:'小李',age:19},
            初始的虚拟DOM:
                <li key=1>小张---18</li>
                <li key=2>小李---19</li>
            更新后的数据:
                <li key=3>小王---20</li>
                <li key=1>小张---18</li>
                <li key=2>小李---19</li>
            更新树后的虚拟DOM:
                <li key=3>小王---20</li>
                <li key=1>小张---18</li>
                <li key=2>小李---19</li>

        */
        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:3,name:'小王',age:20}
                this.setState({persons:[p,...persons]})
            }
            render() {
                return (
                    <div>
                        <h2>展示人员信息</h2>
                        <h3>使用index索引值作为key</h3>
                        <button onClick={this.add}>添加一个小王</button>
                        <ul>
                            {
                                this.state.persons.map((personObj, index)=>{
                                    return <li key={index}>{personObj.name}---{personObj.age}</li>
                                })
                            }
                        </ul>
                        <hr/>
                        <hr/>
                        <h3>使用id(数据的唯一标识)索引值作为key</h3>
                        <ul>
                            {
                                this.state.persons.map((personObj)=>{
                                    return <li key={personObj.id}>{personObj.name}---{personObj.age}</li>
                                })
                            }
                        </ul>
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Person/>, document.getElementById('test1'))
    </script>
</body>
</html>

以上功能相同,但是其性能则相差很大。

有些同学说,我管他性能好坏,我实现功能就行了,这样确实没毛病,但是下面提一个新的需求,则我们不得不考虑两种方式的区别了。

我们在上面例子中<li>组件内添加一个<input>组件 <input type="text"/></li> 。然后我们做如下截图中的操作。

 发现,数据错乱了。由此看出,由什么作为虚拟DOM的key还是很重要的。新旧虚拟DOM进行比较的时候,依据的就是虚拟DOM的key,如果使用了不得当的key,会带来很大的性能问题,而且有时候会发生如上图这种比较严重的数据错乱问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值