0208DOM的diffing算法-React

Posted gaog2zh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了0208DOM的diffing算法-React相关的知识,希望对你有一定的参考价值。

1 React DOM Diffing算法

1.1 原理

React DOM Diffing算法是React用来优化Virtual DOM更新性能的一种算法。当React中的组件状态发生变化时,React会使用Virtual DOM来进行快速的DOM更新。然而,由于Virtual DOM的渲染开销,React需要在Virtual DOM中执行一些优化策略来减少更新次数。

React的DOM Diffing算法的基本思路是比较两个不同状态的Virtual DOM树,找到最小的变化,并将其应用到实际的DOM树中。这个算法将Virtual DOM树分解成一个个的节点,并将它们逐一比较。比较过程中,React会尽可能地复用已有的DOM节点,避免重新创建DOM节点,从而减少渲染的成本。

React的DOM Diffing算法具体的实现步骤如下:

  1. 如果根节点不同,直接替换整个根节点。
  2. 如果节点类型不同,直接替换整个节点。
  3. 如果节点类型和key都相同,则比较节点的属性和子节点。
  4. 如果节点类型相同但key不同,则替换整个节点。
  5. 如果节点类型相同但属性不同,则更新属性。
  6. 如果节点类型相同但子节点不同,则递归比较子节点。

通过这些优化,React可以在保持应用程序的状态更新的同时,避免不必要的DOM操作,提高渲染性能。

1.2 测试

测试代码如下1.2-1所示:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>1301_验证Diffing算法</title>
</head>

<body>
  <div id="test"></div>
  <!-- react核心库 -->
  <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
  <!-- 用于支持react操作DOM -->
  <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
  <!-- 用于将jsx转为js -->
  <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

  <script type="text/babel">

    /**
     * 
     */
    class Time extends React.Component 

      state = date: new Date()
      

      componentDidMount() 
        setInterval(() => 
          // 更新状态
          this.setState(date: new Date())
        , 1000);
      

      /**
       * 初始化渲染,状态更新重新渲染
       */
      render() 
        return (
         <div>
          <h2>hello</h2>
          <input type="text"/> <br/>
          <span>
            现在是:this.state.date.toTimeString()
            <input type="text"/> <br/>
          </span>  
         </div>
        )
      
    

    // 2.渲染虚拟DOM到页面
    ReactDOM.render(<Time/>, document.getElementById('test'))

  </script>
</body>

</html>

测试效果如下图1.2-1所示:

2 React 中key

2.1 key的作用

在React中,key是用于帮助React识别组件中子元素的唯一标识符。当React更新组件时,React会使用key来判断哪些子元素已经发生变化,从而减少不必要的DOM操作,提高渲染性能。

具体来说,当React渲染组件时,它会生成一个Virtual DOM树。Virtual DOM是一个轻量级的JavaScript对象,用于描述实际的DOM结构。当组件的状态发生变化时,React会比较新旧Virtual DOM树之间的差异,然后将差异应用到实际的DOM树上。

在比较Virtual DOM树时,React使用key来识别哪些子元素已经发生变化。如果两个元素具有相同的key,则React会认为它们是同一个元素,并将其重用。如果两个元素的key不同,则React会将其视为两个不同的元素,并重新创建DOM节点。因此,使用正确的key可以帮助React减少DOM操作次数,提高渲染性能。

除了性能方面的考虑,key还可以帮助开发者维护组件的内部状态。在一些需要对组件进行增删操作的场景中,使用key可以确保每个子元素的状态正确地被保留和更新。

Diff对比的最小粒度上标签

2.2 key的取值

在React中,key应该是具有稳定、唯一和可预测性的值。这有助于React识别子元素并减少不必要的DOM操作,提高渲染性能。

通常情况下,key的取值有以下几种方式:

  1. 使用唯一ID(推荐):如果每个子元素都有唯一的ID属性,那么可以使用ID作为key的取值。这样可以确保每个子元素都有一个唯一的key。
  2. 使用索引:如果没有唯一ID,可以使用数组索引作为key的取值。但是,需要注意的是,如果数组中的元素顺序发生变化,那么key也会随之变化,这可能会导致不必要的DOM操作。
  3. 使用唯一的字符串:如果没有唯一ID和数组索引,可以使用唯一的字符串作为key的取值。可以使用UUID或其他生成唯一字符串的方法。
  4. 使用其他唯一属性:如果子元素具有其他唯一属性,例如用户名或电子邮件地址,可以使用这些属性作为key的取值。

需要注意的是,尽管可以使用各种方法生成key的值,但必须确保每个key都是唯一且稳定的。如果key的取值不稳定或重复,可能会导致React无法正确地识别子元素,并且可能会导致不必要的DOM操作,从而降低渲染性能。

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

  1. 若对数据进行: 逆序添加、逆序删除的功能破坏顺序操作:会产生没有必要的真实DOM的更新,即界面没效果,但效率低。
  2. 如果结构中还保护输入类DOM:会产生错误DOM更新
  3. 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅渲染列表用于展示,使用index作为key是没有问题的。

测试代码2.2-1如下所示:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>1301_验证Diffing算法</title>
</head>

<body>
  <div id="test"></div>
  <!-- react核心库 -->
  <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
  <!-- 用于支持react操作DOM -->
  <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
  <!-- 用于将jsx转为js -->
  <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

  <script type="text/babel">

    /**
     * 
     */
    class Person extends React.Component 

      state = 
        persons: [
          id: 1,name: '小张', age: 22,
          id: 2,name: '小王', age: 32,
        ]
      
      

      add = () => 
        const persons = this.state
        const p = id: persons.length+1,name: '小丽', age: 25
        this.setState(persons: [p, ...persons])
      

      /**
       * 初始化渲染,状态更新重新渲染
       */
      render() 
        return (
         <div>
          <h2>展示人员信息</h2>
          <button onClick=this.add>添加新成员</button>
          <h3>使用index(索引)作为key</h3>
          <ul>
            
              this.state.persons.map((p,index)=> 
                return <li key=index>
                  p.name----p.age&nbsp;
                  <input type="text"/>
                </li>
              )
            
          </ul>
          <hr/>
          <h3>使用id(数据唯一标识)作为key</h3>
          <ul>
            
              this.state.persons.map((p)=> 
                return <li key=p.id>
                  p.name----p.age&nbsp;
                  <input type="text"/>
                </li>
              )
            
          </ul>
         </div>
        )
      
    

    // 2.渲染虚拟DOM到页面
    ReactDOM.render(<Person/>, document.getElementById('test'))

  </script>
</body>

</html>
  • index作为key逆序添加导致全部已有标签的key+1,改变;那么对比相同key时,标签内容不一致,会重新渲染。所以页面上好像没啥问题,但是效率很低。
  • 如果有子元素,会导致数据错乱。

添加新元素前效果如下下2.2-1所示:

添加新成员后效果图示如下2.2-2所示:

结语

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/react-study

参考:

[1]React视频教程[CP/OL].2020-12-15.p48.

[2]React官网[CP/OL].

[2]ChatGPT[CP/OL].

以上是关于0208DOM的diffing算法-React的主要内容,如果未能解决你的问题,请参考以下文章

React组件的生命周期 - 虚拟DOM - DOM Diffing算法

React的diffing算法(面试题)

React的diffing算法(面试题)

React的diffing算法(面试题)

diffing算法以及key值的作用

React入门