第十四章 DOM的Diff算法与key

简介: 第十四章 DOM的Diff算法与key

React使用Diff算法来比较虚拟DOM树和真实DOM树之间的差异,并仅更新必要的部分,以提高性能。key的作用是在Diff算法中帮助React确定哪些节点已更改,哪些节点已添加或删除。

我们以案例来说明。

使用索引值和唯一ID作为key的效果

  • 1、使用索引值作为key
class Person extends React.Component {
        state = {
          persons: [
            {id:1,name:'张三',age: 20},
            {id:2,name:'李四',age: 21},
          ]
        }
        // 增加人员
        addPerson = () => {
          let {persons} = this.state
          const user = {id:persons.length + 1,name:'王麻子',age:22}
          persons = [user,...persons]
          this.setState({persons})
        }
        render(){
          const {persons} = this.state
          return (
            <div>
              <h1>验证diff算法</h1>
              <button onClick={this.addPerson}>增加人员</button>
              {
                persons.map((val,idx)=> {
                  const info = "姓名:"+val.name+ "===年龄:" + val.age
                  console.log(idx,info)
                  return (
                    <div key={idx}>{info}</div>
                  )
                })
              }
            </div>
          )
      }
 }
      // 2、将虚拟DOM渲染到页面,标签必须闭合
      ReactDOM.render(<Person/>, document.getElementById('app'))

以上代码我们使用的是索引值idx来作为标签的key值,渲染到页面,当我点击【增加人员】的按钮时,会在persons的状态值里面最前方插入一个新的人员信息,然后reactrender到页面中去。其页面效果如下:image.png

  • 2、使用id作为key值
      class Person extends React.Component {
        state = {
          persons: [
            {id:1,name:'张三',age: 20},
            {id:2,name:'李四',age: 21},
          ]
        }
        // 增加人员
        addPerson = () => {
          let {persons} = this.state
          const user = {id:persons.length + 1,name:'王麻子',age:22}
          persons = [user,...persons]
          this.setState({persons})
        }
        render(){
          const {persons} = this.state
          return (
            <div>
              <h1>验证diff算法</h1>
              <button onClick={this.addPerson}>增加人员</button>
              {
                persons.map((val,idx)=> {
                  const info = "姓名:"+val.name+ "===年龄:" + val.age
                  console.log(idx,info)
                  return (
                    <div key={val.id}>{info}</div>
                  )
                })
              }
            </div>
          )
        }
      }
      // 2、将虚拟DOM渲染到页面,标签必须闭合
      ReactDOM.render(<Person/>, document.getElementById('app'))

以上代码我们使用id来作为标签的key值,但是这里的效果和我们看到的是一样的,但是在react中处理的方式是不一样的,后续我们继续讨论。image.png

使用索引值和唯一ID作为key的区别

我们对以上案例做一下修改,我们增加一个输入框,在看看其效果。

  • 1、使用索引值作为key
class Person extends React.Component {
  state = {
    persons: [
      {id:1,name:'张三',age: 20},
      {id:2,name:'李四',age: 21},
    ]
  }
  // 增加人员
  addPerson = () => {
    let {persons} = this.state
    const user = {id:persons.length + 1,name:'王麻子',age:22}
    persons = [user,...persons]
    this.setState({persons})
  }
  render(){
    const {persons} = this.state
    return (
    <div>
      <h1>验证diff算法</h1>
      <button onClick={this.addPerson}>增加人员</button>
      {
        persons.map((val,idx)=> {
          const info = "姓名:"+val.name+ "===年龄:" + val.age
          console.log(idx,info)
          return (
            <div key={idx}>{info} <input defaultValue={info} type="text"/></div>
                )
              })
            }
          </div>
        )
      }
    }
    // 2、将虚拟DOM渲染到页面,标签必须闭合
    ReactDOM.render(<Person/>, document.getElementById('app'))

直接看效果:image.png

姓名:王麻子===年龄:22 ----输入框:姓名:张三===年龄:20
姓名:张三===年龄:20   ----输入框:姓名:李四===年龄:21
姓名:李四===年龄:21   ----输入框:姓名:李四===年龄:21

在以上效果图上我们发现了严重的错误:输入框的内容与其人员信息不一致,这是为什么呢?

  • 2、使用ID作为key
  class Person extends React.Component {
   state = {
     persons: [
       {id:1,name:'张三',age: 20},
       {id:2,name:'李四',age: 21},
     ]
   }
   // 增加人员
   addPerson = () => {
     let {persons} = this.state
     const user = {id:persons.length + 1,name:'王麻子',age:22}
     persons = [user,...persons]
     this.setState({persons})
   }
    render(){
      const {persons} = this.state
      return (
        <div>
          <h1>验证diff算法</h1>
          <button onClick={this.addPerson}>增加人员</button>
          {
            persons.map((val,idx)=> {
              const info = "姓名:"+val.name+ "===年龄:" + val.age
              console.log(idx,info)
              return (
                <div key={val.id}>{info} <input defaultValue={info} type="text"/></div>
                  )
                })
              }
            </div>
          )
        }
      }
      // 2、将虚拟DOM渲染到页面,标签必须闭合
      ReactDOM.render(<Person/>, document.getElementById('app'))

直接看效果:image.png

姓名:王麻子===年龄:22 ----输入框:姓名:王麻子===年龄:22
姓名:张三===年龄:20   ----输入框:姓名:张三===年龄:20
姓名:李四===年龄:21   ----输入框:姓名:李四===年龄:21

在以上效果图上我们发现了人员信息与输入框的信息一致,并没有发生什么错误,这是为什么呢?


分析索引值与ID值作为key的原理

  • 分析key值是索引值时的流程image.png

根据上图我们可以知道使用索引值作为key的时候,三条数据基本上都要生成新的DOM,而输入框的值因为与旧的虚拟DOM比较内容一致,导致与新的数据不一致的结果。

  • 分析key值是ID值的流程image.png

根据上图我们可以知道使用唯一ID作为key值时,比较第一条数据时key值就不存在需要生成新的虚拟DOM,而后面两条的key值与旧的虚拟DOM一致,可以复用旧的真实DOM且不需要生成新的DOM,减少成本,这样使得性能更好。

小总结

结果案例演示我们知道为什么遍历列表时,key最好不要用index,而是使用唯一标识,以此来减少成本,提高性能。

  • 虚拟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可能会引发的问题

1、若对数据进行:逆序添加、逆序删除等破坏顺序操作:

   会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

2、如果结构中还包含输入类的DOM:

   会产生错误DOM更新 ==> 界面有问题。

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

  • 开发中如何选择key?

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

2、如果确定只是简单的展示数据,用index也是可以的。

相关文章
|
2月前
|
JavaScript 前端开发 算法
MVVM模型,虚拟DOM和diff算法
1.MVVM是前端开发领域当中非常流行的开发思想。(一种架构模式)目前前端的大部分主流框架都实现了这个MVVM思想,例如Vue,React等2.虽然Vue没有完全遵循MVVM模型,但是Vue的设计也受到了它的启发。Vue框架基本上也是符合MVVM思想的 3.MVVM模型当中尝到了Model和View进行了分离,为什么要分离?
|
2天前
|
JavaScript 算法 前端开发
【专栏】简单理解 slot 算法和 shadow DOM
【4月更文挑战第29天】本文探讨了前端开发中的slot算法和shadow DOM,两者提供更灵活、高效和模块化的开发方式。slot算法允许在组件中定义插槽位置,实现内容的灵活插入和复用,提高代码可读性和维护性。shadow DOM则通过封装DOM子树,实现样式和事件的隔离,增强组件独立性和安全性。这两种技术常应用于组件开发、页面布局和主题定制,但也面临兼容性、学习曲线和性能优化等挑战。理解并掌握它们能提升开发效率和用户体验。
|
29天前
|
JavaScript 算法 前端开发
什么是虚拟DOM?什么是diff算法?
什么是虚拟DOM?什么是diff算法?
|
2月前
|
缓存 JavaScript 算法
Vue.js中的diff算法:让虚拟DOM更高效
Vue.js中的diff算法:让虚拟DOM更高效
|
2月前
|
机器学习/深度学习 算法
m基于深度学习的64QAM调制解调系统频偏估计和补偿算法matlab仿真
### 算法仿真结果 展示5张图像,描绘了基于深度学习的频偏估计和补偿在MATLAB 2022a中的仿真效果。 ### 理论概要 - 深度学习算法用于建立信号与频偏的非线性映射,无需导频,节省资源。 - 网络模型(如CNN或RNN)处理IQ数据,提取特征,简化估计补偿过程,降低复杂度。 - 64QAM系统中,通过神经网络实现精确频偏感知,增强通信性能。 ### MATLAB核心程序 - 代码生成64QAM信号,模拟不同SNR和频偏条件,使用深度学习进行相位估计和补偿。 - 仿真比较了有无补偿的误码率,显示补偿能显著改善通信质量。 ```
33 1
|
10天前
|
机器学习/深度学习 人工智能 算法
基于DCT和扩频的音频水印嵌入提取算法matlab仿真
本文介绍了结合DCT和扩频技术的音频水印算法,用于在不降低音质的情况下嵌入版权信息。在matlab2022a中实现,算法利用DCT进行频域处理,通过扩频增强水印的隐蔽性和抗攻击性。核心程序展示了水印的嵌入与提取过程,包括DCT变换、水印扩频及反变换步骤。该方法有效且专业,未来研究将侧重于提高实用性和安全性。
|
25天前
|
机器学习/深度学习 算法
【MATLAB】GA_BP神经网络时序预测算法
【MATLAB】GA_BP神经网络时序预测算法
35 8
|
29天前
|
机器学习/深度学习 算法 Serverless
【MATLAB】PSO_BP神经网络回归预测算法(适用光伏发电回归预测等)
【MATLAB】PSO_BP神经网络回归预测算法(适用光伏发电回归预测等)
30 1
|
1天前
|
算法 TensorFlow 算法框架/工具
基于直方图的图像阈值计算和分割算法FPGA实现,包含tb测试文件和MATLAB辅助验证
这是一个关于图像处理的算法实现摘要,主要包括四部分:展示了四张算法运行的效果图;提到了使用的软件版本为VIVADO 2019.2和matlab 2022a;介绍了算法理论,即基于直方图的图像阈值分割,通过灰度直方图分布选取阈值来区分图像区域;并提供了部分Verilog代码,该代码读取图像数据,进行处理,并输出结果到&quot;result.txt&quot;以供MATLAB显示图像分割效果。
|
1天前
|
算法 搜索推荐 数据挖掘
MATLAB模糊C均值聚类FCM改进的推荐系统协同过滤算法分析MovieLens电影数据集
MATLAB模糊C均值聚类FCM改进的推荐系统协同过滤算法分析MovieLens电影数据集