React中diffing算法

284 阅读4分钟

React中diffing算法

1:虚拟dom中key的作用?

  • 1) 简单的说key是虚拟DOM对象的标识,在更新显示时起到极其重要的作用。

  • 2) 详细的说:当状态中的数据发生变化时,react会更具【新数据】生成新的【新的虚拟DOM】,然后React将进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:

    • a)在【旧虚拟DOM】中找到了【新虚拟DOM】相同的key

      • 若虚拟DOM中内容没有改变,直接使用之前的【真实DOM】.
      • 若虚拟DOM中的内容改变,则生成新的【真实DOM】,随后替换掉页面上之前的真实DOM
    • b) 【旧虚拟DOM】中未找到与【新虚拟DOM】相同的key

      • 更具新数据生成新的真实DOM,然后渲染到页面上。

2:index作为key可能引发的问题?

  • 1) 若对数据进行逆序删除、添加等破坏顺序操作:
    • a)会产生没必要的【真实DOM】的更新,界面没有问题,但效率低。
 <body>

    <div id="test"></div>
    <script type="text/babel">

    // 创建类组件
    class MyComponent extends React.Component {
        state = {
            persons:[
                {name:'张三',age:20},
                {name:'李四',age:22}
            ]
        }

        addPerson = ()=> {
            let {persons} = this.state
            let addDate = {name: '老王',age: persons.length+1}
            this.setState({
                persons: [addDate,...persons] // 把新添加的数据添加到数组的第一个位置
            })
        }
        render() {
            let {persons} = this.state
            return (
                <div>
                    <p><button onClick={this.addPerson}>添加一个老王</button></p>
                    <ul>
                        {
                            persons.map((item,index) => {
                                return (
                                    <li key={index}>
                                        <span>姓名:{item.name}----年龄:{item.age}</span>
                                    </li>
                                )
                            }) 
                        }
                    </ul>
                </div>
            )
        }
    }

    // 渲染
    ReactDOM.render(<MyComponent/>,document.getElementById('test'))

    </script>
</body>

分析:

  • 1:数据
   {name:'张三',age:20},
   {name:'李四',age:22}
  • 2:初始化的虚拟DOM
  <li key=0> <span>姓名:张三----年龄:20</span> </li>
  <li key=1> <span>姓名:李四----年龄:22</span> </li>
  • 3:更新后的数据
  {name:'老王',age:3},
  {name:'张三',age:20},
  {name:'李四',age:22}
  • 4:更新后的虚拟DOM
 <li key=0> <span>姓名:老王----年龄:3</span> </li>
 <li key=1> <span>姓名:张三----年龄:20</span>  </li>
 <li key=2> <span>姓名:李四----年龄:22</span> </li>
  • 5:去旧的虚拟DOM找,是否有相同的key
 <li key=0> <span>姓名:张三----年龄:20</span> </li>
 <li key=0> <span>姓名:老王----年龄:3</span> </li>

发现【新虚拟DOM中的key】和【旧虚拟DOM中的key】 都为0,相等则比较内容,但发现内容不相同,则生成新的【真实DOM】<li key=0> <span>姓名:老王----年龄:3</span> </li>

   <li key=1> <span>姓名:李四----年龄:22</span> </li>
   <li key=1> <span>姓名:张三----年龄:20</span>  </li>

同样的,在key为1的时候,【旧虚拟DOM】也存在,且内容不一样,那么也是以【新的虚拟DOM】为准,生成【新的真实DOM】

  <li key=2> <span>姓名:李四----年龄:22</span> </li>

key等于2的时候,发现【旧虚拟DOM】中不存在key等于2的DOM,那么生成【新的真实DOM】 <li key=2> <span>姓名:李四----年龄:22</span> </li>

所以,本来以下这两个DOM元素是不必更新的,可以复用的,但却因为key的顺序已经被打乱了,导致页面的真实DOM被更新。

<li key=0> <span>姓名:张三----年龄:20</span> </li>
<li key=1> <span>姓名:李四----年龄:22</span> </li>
  • 如果使用id唯一值作为key,会发生几次更新呢?

    • 数据
    {id:0,name:'张三',age:20},
    {id:1,name:'李四',age:22}
    
    • 2:初始化的虚拟DOM
      <li key=0> <span>姓名:张三----年龄:20</span> </li>
      <li key=1> <span>姓名:李四----年龄:22</span> </li>
    
    • 3:更新后的数据
      {id:2,name:'老王',age:3},
      {id:0,name:'张三',age:20},
      {id:1,name:'李四',age:22}
    
    • 4:更新后的虚拟DOM
     <li key=2> <span>姓名:老王----年龄:3</span> </li>
     <li key=0> <span>姓名:张三----年龄:20</span>  </li>
     <li key=1> <span>姓名:李四----年龄:22</span> </li>
    
    • 5:去旧的虚拟DOM找,是否有相同的key 发现只有<li key=2> <span>姓名:老王----年龄:3</span> </li>不一样,需要更新【真实DOM】。但其他两个是一样的,且内容也一样,可以复用不必更新DOM。所以,更新的次数就少了很多,效率得到提高。
  • 2) 如果结构中还包括输入类的DOM操作:

    render() {
            let {persons} = this.state
            return (
                <div>
                    <p><button onClick={this.addPerson}>添加一个老王</button></p>
                    <ul>
                        {
                            persons.map((item,index) => {
                                return (
                                    <li key={index}>
                                        <span>姓名:{item.name}----年龄:{item.age}</span>
                                        <input type="text"/> // 加了一个输入框
                                    </li>
                                )
                            }) 
                        }
                    </ul>
                </div>
            )
        }
    

    • a)会产生错误DOM更新,页面会有问题

    数据发生错乱,那是因为在比较<input type="text"/>的时候,标签和属性都是一样的,那么就会复用,但是输入框依然残留有之前的数据,所以,我们看到输入框不变内容依旧不变。

  • 3) 注意: 如果不对数据进行逆序删除、逆序添加等破坏顺序操作,仅仅用于渲染列表展示,那么index作为key是没有任何问题的。

3:开发中如何选择key

  • 最好使用每条数据唯一的标识作为key,比如ID、身份证号、学号等唯一值。

  • 如果只是简单的数据展示,使用index也是可以的。