后端程序员入门react笔记——react的diff算法(三)

diffing算法

虚拟dom

我们知道,react里面操作的都是虚拟dom,最后经过render渲染为真正的dom,那么为什么要提出虚拟dom这个概念呢?其实就是将逻辑和视图区分开,react的虚拟dom,就相当于mvc的c,将数据逻辑和真正的dom区分开,我们知道,对于前端来说dom操作是非常昂贵的,性能消耗最大的就是dom操作。而virtual dom减少了对dom的操作,,不仅避免了资源浪费,而且页面的构建也得到了很大的提升。

为什么要diff

前面我们说了,应避免过多的操作dom,那么diff就是解决了这种问题。我们知道,react在状态发生变化的时候,会批量更新dom,生成新的UI,但是难道state的某一个值发生变化就要导致整个dom重新渲染吗?这明显是不科学的,为了解决这种问题,react提出了diff算法,通过对比新旧的两个虚拟dom树来检测那些dom是真的需要重新如安然,哪些dom是没有变化的。

diff算法

传统的diff算法是通过循环递归的方式对节点一次进行对比,需要O(n ^3)的时间复杂度。为什么?我们从最简单的说,把一棵树转换为另外一棵树,其实就是把一颗n个节点的树挨个儿去另一棵n个节点的树中查找,整个过程是n*n,那么如果发现有不同的地方,我们就需要需改,对一棵树的增删改的算法复杂度是n,那么整个过程就是n*n*n
在这里插入图片描述
react将这种对比策略做了一个优化,将复杂度降到了O(n),那么react是怎么实现的呢?react的diff会预设三个限制

  • 只进行同层级比较
  • 新旧节点的type不同,直接删除旧节点,创建新节点,比如组件不同,元素的类型不同,原来是ul,里面是li,后来改成了div+p,这个时候就会删除旧dom,创建新的dom.
  • 通过key来复用节点

在上面三个限制的基础上,对tree,conponent,element的处理方式又做了优化

  • dom节点不一致直接删除旧节点,创建新节点
  • 组件类型不一致直接删除组件下所有节点,创建新节点
  • 同一层的dom元素,以每个元素对应的key为标识,提供三种操作方式,删除新建和移动
    在这里插入图片描述

diff的最小颗粒度

diff的最小颗粒度是标签,我们举例看一下

class Hello extends React.Component {
   
   state={
   
       time:new Date()
   }
   componentDidMount(){
   
       this.timer=setInterval(()=>{
   
           this.setState({
   
               time:new Date()
           })
       },5000)
   }
   render() {
   
       console.log("i am render")
       return (
           <ul>
               <li>备注:<input type="text"/></li>
               <li><span>{
   this.state.time.toTimeString()}</span></li>
           </ul>
       )
   }
}

我们看到,每隔5秒,span标签就会从新刷新一次,而其他元素没有变化
在这里插入图片描述

key

前面我们说了,同一层级的元素如果发生了变化,可以删除 插入和移动,前提是必须有key作为标识,如果没有key,比如下面代码

class Hello extends React.Component {
   
    state = {
   
        heros: [
            {
   
                id: 1,
                name: "张三"
            },
            {
   
                id: 2,
                name: "李四"
            }
        ]
    }
    render() {
   
        console.log("i am render")
        return (
            <ul>
                {
   this.state.heros.map((hero,index)=>{
   
                    return <li>{
   hero.name}</li>
                })}
            </ul>
        )
    }
}

这个时候浏览器提示了一个warm ,意思是每一个child都应该有唯一key。
在这里插入图片描述
那么这个key应该怎么选取呢

  • 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
  • 如果确定只是简单的展示数据,用index也是可以的。 但是不推荐。
    为什么不推荐index作为key,官方文档说,如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。

我们来举个例子对比一下,再分析问题所在。这段代码里,我点击添加按钮的时候,改变了原来数组对象的顺序,在数组的前头添加了一个对象,我们点击看一下效果

class Hello extends React.Component {
   
    state = {
   
        heros: [
            {
   
                id: 1,
                name: "张三"
            },
            {
   
                id: 2,
                name: "李四"
            }
        ]
    }
    addHero=()=>{
   
        this.setState({
   
            heros: [
                {
   
                    id: 3,
                    name: "王五"
                },
                ...this.state.heros
            ]
        })
    }
    render() {
   
        console.log("i am render")
        return (
            <ul>
                <button onClick={
   this.addHero}>点击添加王五</button>
                <h1>index as key</h1>
                {
   this.state.heros.map((hero,index)=>{
   
                    return <li key={
   index}>{
   hero.name}<input type="text" /></li>
                })}
                <h1>age as key</h1>
                {
   this.state.heros.map((hero,index)=>{
   
                    return <li key={
   hero.id}>{
   hero.name}<input type="text" /></li>
                })}
            </ul>
        )
    }
}

在这里插入图片描述
我们发现了什么,以index做为key,input框并没有跟着往下移动,id作为key的往下移动了,我们发现问题了,那么为什么index作为key,input框不往下移动呢?就是因为数据的顺序发生变化了,王五被塞进了数组对象的头部,但是diff的时候,由于元素的key是index,还是从0开始的,并没有发生变化,所以input框并不会移动。

相关推荐

  1. 程序员入门react笔记(七)- React路由

    2024-02-22 15:06:01       40 阅读
  2. React diff 算法

    2024-02-22 15:06:01       40 阅读
  3. reactdiff算法

    2024-02-22 15:06:01       54 阅读
  4. ReactDiff算法

    2024-02-22 15:06:01       35 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-22 15:06:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-22 15:06:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-02-22 15:06:01       82 阅读
  4. Python语言-面向对象

    2024-02-22 15:06:01       91 阅读

热门阅读

  1. 路由器配置DMZ主机映射

    2024-02-22 15:06:01       73 阅读
  2. 提升爬虫效率:多线程,多进程,多协程

    2024-02-22 15:06:01       45 阅读
  3. 近红外γ-谷氨酰转肽酶(GGT)荧光探针(NRh-G)星戈瑞

    2024-02-22 15:06:01       51 阅读
  4. 物联网和人工智能的融合

    2024-02-22 15:06:01       60 阅读
  5. android 全局异常处理封装

    2024-02-22 15:06:01       51 阅读
  6. C语言内存模型的深度剖析

    2024-02-22 15:06:01       51 阅读
  7. 自然语言转SQL的应用场景探索

    2024-02-22 15:06:01       50 阅读
  8. 剑指offer面试题17 合并俩个排序的列表

    2024-02-22 15:06:01       49 阅读
  9. sqlserver 函数

    2024-02-22 15:06:01       50 阅读
  10. 算法:带权重随机算法

    2024-02-22 15:06:01       49 阅读
  11. spring和springboot的区别,简单直接

    2024-02-22 15:06:01       51 阅读