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].

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值