• 后端程序员入门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>
           )
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    我们看到,每隔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>
            )
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这个时候浏览器提示了一个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>
            )
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

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

  • 相关阅读:
    团队规范之Git规范
    用Python实现一个全网可下载的Linux命令流程
    【Redis】.net core Redis事件订阅与发布,基础篇
    【微服务~Nacos】Nacos服务提供者和服务消费者
    WPF入门教程系列二十八 ——DataGrid使用示例MVVM模式(6)
    Pandas+Pyecharts | 2022年世界500强数据分析可视化
    TypeScript_面向对象
    AI顶会ICLR‘23作者竟让审稿人重读幼儿园? 大会主席强硬回复!
    Java面试八股文 2021年最新Java面试题及答案汇总
    比特币的蒙提霍尔问题
  • 原文地址:https://blog.csdn.net/qq_39962403/article/details/136171798