当前位置: 首页 > news >正文

后端程序员入门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框并不会移动。

相关文章:

  • Python异常处理知识点汇总,五分钟就能学会
  • Spring boot 实现监听 Redis key 失效事件
  • 从新手到专家:AutoCAD 完全指南
  • Bicycles(变形dijkstra,动态规划思想)
  • 【大厂AI课学习笔记NO.53】2.3深度学习开发任务实例(6)数据采集
  • Git+py+ipynb Usage
  • 消息中间件篇之RabbitMQ-高可用机制
  • Unity(第五部)新手图层和标签的理解
  • http 状态码
  • 贪心算法练习day2
  • C++基础知识(六:继承)
  • Unity(第八部)Vector3的三维向量和旋转(坐标和缩放也简单讲了一下)
  • K8s二进制安装部署
  • Apache软件基金会的孵化标准和毕业标准
  • Flask中使用日志库loguru
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • CentOS7简单部署NFS
  • CSS居中完全指南——构建CSS居中决策树
  • FastReport在线报表设计器工作原理
  • Java新版本的开发已正式进入轨道,版本号18.3
  • Python 基础起步 (十) 什么叫函数?
  • Python3爬取英雄联盟英雄皮肤大图
  • React as a UI Runtime(五、列表)
  • REST架构的思考
  • SpiderData 2019年2月23日 DApp数据排行榜
  • 番外篇1:在Windows环境下安装JDK
  • 关于for循环的简单归纳
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 使用权重正则化较少模型过拟合
  • 延迟脚本的方式
  • 移动端解决方案学习记录
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • const的用法,特别是用在函数前面与后面的区别
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • #QT(智能家居界面-界面切换)
  • (1)常见O(n^2)排序算法解析
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • .net 7 上传文件踩坑
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .NET Micro Framework初体验(二)
  • .NET Remoting学习笔记(三)信道
  • .NET 设计一套高性能的弱事件机制
  • .net6使用Sejil可视化日志
  • .NET多线程执行函数
  • @RequestMapping 的作用是什么?