后端程序员入门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的最小颗粒度是标签,我们举例看一下

js 复制代码
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,比如下面代码

js 复制代码
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 值,因为这样做会导致性能变差,还可能引起组件状态的问题。

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

js 复制代码
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框并不会移动。

相关推荐
啊阿狸不会拉杆35 分钟前
《算法导论》第 32 章 - 字符串匹配
开发语言·c++·算法
小学生的信奥之路1 小时前
洛谷P3817题解:贪心算法解决糖果分配问题
c++·算法·贪心算法
emojiwoo2 小时前
【前端基础知识系列六】React 项目基本框架及常见文件夹作用总结(图文版)
前端·react.js·前端框架
你知道网上冲浪吗2 小时前
【原创理论】Stochastic Coupled Dyadic System (SCDS):一个用于两性关系动力学建模的随机耦合系统框架
python·算法·数学建模·数值分析
yatingliu20192 小时前
HiveQL | 个人学习笔记
hive·笔记·sql·学习
郭庆汝2 小时前
CMake概述用法详细笔记
笔记
张人玉2 小时前
XML 序列化与操作详解笔记
xml·前端·笔记
风和日丽 随波逐流2 小时前
java17学习笔记-Deprecate the Applet API for Removal
笔记·学习
淮北也生橘122 小时前
Linux的ALSA音频框架学习笔记
linux·笔记·学习
地平线开发者3 小时前
征程 6 | PTQ 精度调优辅助代码,总有你用得上的
算法·自动驾驶