react 虚拟DOM及diff算法

什么是Virtual DOM?为什么要使用它?

在vue,react这些框架中,都是通过监听数据的变化而相应的去渲染我们的视图.但是如果每次修改数据就去操作一次DOM树,这在性能上无疑是大打折扣的.虚拟DOM的出现就是为了解决这个问题,并且起到协调的作用.本质上就是在js和真实DOM树之间做了一个缓存,具体原理是:每次当数据发生变化时,渲染机制先在虚拟DOM上去改变视图,然后再跟实际DOM树作比较(作比较用到的是虚拟DOM的diff算法),再在实际DOM上更新差异部分(也就是这个时候才去操作实际DOM树,渲染差异部分),这样就减少了对实际DOM树的操作,达到性能优化的效果.

简单理解为:虚拟DOM树其实是实际DOM树更改之前的参考对象,是js实际操作DOM节点时与真实DOM树之间的一个中介(缓存).以达到减少对DOM节点的频繁无效的操作和性能优化的目的.

创建虚拟DOM

两种方式:
1.纯JS (一般不用这种)
2.JSX

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
   <!-- 由于浏览器的js引擎不能直接解析JSX.需要babel.js转译为纯js代码 -->
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <title>react</title>
</head>
<body>
// 创建虚拟DOM容器
  <div id="myVirt"></div> 
  <div id="myVirt2"></div>
  <script type="text/babel">  // 使用jsx创建虚拟DOM时,type不能少
    
     // 纯js创建虚拟DOM           创建标签       属性                      文本内容   
    var str = React.createElement('div',{id:"myVirt",className:'myVirt'},'JS创建虚拟DOM');
    console.log(str,1234)
  
    // 渲染虚拟DOM:ReactDOM.render(virtualDOM,containerDOM)
    ReactDOM.render(str,document.getElementById("myVirt"))
    
    var text = "我是JSX虚拟DOM";
    var viturlDiv = <div className="myVirt2">{text}</div>
    // JSX创建虚拟DOM
    ReactDOM.render(viturlDiv,document.getElementById("myVirt2"))

  </script>
  
</body>
</html>

如何实现虚拟DOM与实际DOM之间的比较呢?

Diff算法:该算法并不是react独有的算法,react只是在传统diff算法的基础上做了一些优化而已.实现机制是:深度遍历DOM树,从根节点开始优先查找节点的左子树及其左节点,完全遍历完左子树之后才会开始去查找右子树,查找右子树时也是先从右子树的左节点开始往下查找,直到遍历完全.比较出两棵树之间的差异,然后在真实DOM树更新差异部分.

diff算法的比较规则:

  1. 节点不存在,直接创建该节点;
  2. 节点存在且节点类型相同,继而判断属性是否相同,不同则修改节点属性,无需删除该节点
  3. 节点存在且节点类型相同,继而判断文本内容是否相同,有差异则做出修改.
  4. 节点类型不同,直接删除元素及其子节点,替换成新节点

两个节点之间的差异包括:
1.直接替换原有节点
2.调整子节点,包括移动删除等操作
3.修改节点属性,文本内容

react diff:tree diff component diff element diff

官方建议不要出现跨层级的节点操作,当出现节点跨层级移动时,react并不会去移动节点,而是删除要移动的节点及其子节点并重新创建到移动位置,

场景假设:
假如我有一千个节点,当我删除第一个节点时,那我要去更新剩下的999个节点的位置,针对相同的节点只是位置发生变化就会出现大量的删除/创建节点的操作,这无疑会影响到react的性能.

*针对这一问题,react diff做出的优化措施是:*对同一层级的同组子节点(也就是父节点下的所有子节点)添加唯一的key来进行区分.

其实针对相同节点的操作,如果我有唯一的key值,我就根据这个key值去定位需要操作的节点,执行删除操作即可,也不会影响到其他节点.如果要移动节点之间的位置,同理根据key值找到对应的节点,交换位置并更新为新的节点集合即可,其他节点不需要发生任何操作.这样来达到优化.

key的作用主要是:

1.准确判断除当前节点是否在旧集合中
2.极大的减少遍历次数

当然也不是所有都加上key就是性能优化了,对于简单列表页渲染来说,不加key要比加了key的性能好.

例如:

<div>1</div>          <div key="1">1<div>
<div>2</div>          <div key="2">2<div>
<div>3</div>          <div key="3">3<div>
<div>4</div>          <div key="4">4<div>
<div>5</div>          <div key="5">5<div>

我如果要修改节点2跟4的位置,没有key值的话我直接修改InnerText即可.加了key值之后我需要去找到key=2的节点然后再找到key=4的节点,然后再将两个节点调换位置.这样移动DOM节点的操作性能上就不如直接修改InnerText.

所以,请注意不是加了key就一定优化性能.

参考大佬博客:https://segmentfault.com/a/1190000018914249

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值