React的虚拟DOM

Stella981
• 阅读 701

上一篇文章中,DOM树的信息可以用JavaScript对象来表示,反过来,可以根据这个用JavaScript对象表示的树结构来真正构建一颗DOM树。 用JavaScript对象表示DOM信息和结构,当状态变更的时候,重新渲染这个JavaScript的对象结构,当然这样做,其实并没有更新到真正的页面上。

但是可以用新渲染的对象树和旧的树进行对比,记录这两棵树的差异。记录下来的不同就是我们需要对页面真正的DOM操作,然后把它们应用在真正的DOM树上,页面才会真正变更。

###Virtual DOM算法的实现步骤 1.用JavaScript对象结构表示DOM树的结构,然后用这个树构建一个真正的DOM树,插到文档中。

//函数参数的定义,解构赋值中的一个用途
function Element({tagName,props,children}){
    if(!(this instanceof Element)){
        return new Element({tagName,props,children});
    }

    this.tagName = tagName;
    this.props = props || {};
    this.children = children || [];
}

2.当状态变化的时候,重新构建一颗新的对象树,然后用新的树和旧的树进行比较,记录两颗树的差异。 3.把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。 Virtual DOM实质上是在JS和DOM之间做了一个缓存,可以类比cpu和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存,既然DOM这么慢,就在它们JS和DOM之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。

####步骤一:见前一篇博客 ####步骤二:比较两颗虚拟DOM树的差异 1.深度优先遍历,记录差异 在实际的代码中,会对新旧两颗树进行一个深度优先的遍历,这样每个节点都会有一个唯一的标记。 React的虚拟DOM

在深度优先遍历的时候,每当遍历到一个节点的时候就会和新的DOM树进行比较,如果有差异,就会记录到一个对象里面。 2.这里会涉及到差异类型 (1)替换掉原来的节点,例如把上面的div换成了section (2)移动、删除、新增子节点 (3)修改节点的属性 (4)对于文本节点,文本内容可能会改变。

####步骤三:把差异引用到真正的DOM树上 React的虚拟DOM

因为步骤一所构建的JavaScript对象树和render出来的真正的DOM树的信息、结构是一样的。所以我们可以对那颗DOM树也进行深度优先遍历,遍历的时候从步骤二生成的差异对象中找出当前遍历的节点差异,然后进行DOM操作。 ####虚拟DOM算法的思想 标准的DOM机制是用户在应用上的操作实际反映的是直接对真实的dom进行操作,而在React中,用户在应用中对dom的操作实际上是对虚拟dom的操作,用户的操作产生的数据改变或者state变量改变都会应用到虚拟dom上,之后再批量的对这些更改进行diff算法计算,对比操作前后的虚拟dom树,把更改后的变化再同步到真实dom上。(在虚拟dom上执行多次修改,在真实dom中,只会执行一次dom操作,因为在React虚拟dom机制中,它会把所有的操作都合并,只会对比刚开始的状态和最后操作的状态,两者中找出不同,然后再同步到真实dom中。)

####react的独特的diff算法 diff算法的处理方法,对操作前后的dom树的同一层的节点进行对比,一层一层对比。,这样时间复杂度就为o(n)。 React的虚拟DOM

####比较两颗虚拟DOM树的差异

在用js对象表示DOM结构后,当页面状态发生变化时需要操作DOM的时候,我们可以先通过虚拟DOM计算出新的DOM树和旧的DOM树之间的最小修改量,然后最后一次性的将差异修改到真实DOM上。 下图是一个简单的两个虚拟DOM之间的差异 React的虚拟DOM 但是真实的场景下的DOM结构很复杂,我们必须借助于一个有效的DOM树比较算法。 -(1)如何比较两个DOM树

  • (2)如何记录节点之间的差异 #####如何比较两个DOM树

计算两颗树之间的差异的常规算法复杂度是O(n的3次方),一个文档的DOM结构有上百个节点是正常的情况,这种复杂度不能应用于实际项目。所以,针对前端的具体情况,我们很少跨级别的修改DOM节点,通常是修改节点的属性、调整子节点的顺序、添加子节点等。所以我们一般对同级别节点进行比较,避免了diff算法的复杂性。对同级节点进行比较的常用方法就是深度优先遍历。在深度优先遍历过程中,每个节点都会有一个唯一的标记。在深度优先遍历的时候,每遍历到一个节点就把该节点和新的树进行对比。如果有差异就记录到一个对象里面。

#####如何记录节点之间的差异 由于我们对DOM采取的是同级比较,因此节点之间的差异可以归结为四种类型

  • 修改节点属性:用PROPS表示

  • 修改文本节点内容: 用TEXT表示

  • 替换原有节点,用REPLACE表示

  • 调整子节点,包括移动、删除、添加等,用RECODER表示 对于节点之间的差异,可以很方便的使用上述四种方式进行记录,比如当旧节点被替换的时候:

    {type:REPLACE,node newNode}

当旧节点的属性修改时

{type:PROPS,props newProps}

每个节点都有一个编号,如果对应的节点有变化,那么就把相应变化的类别记录下来即可。最后将这个差异对真实DOM做最小化的修改。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皮卡皮卡皮 皮卡皮卡皮
2年前
javaScript. Dom 基本操作
DOM节点查找jsdocument.getElementById()//通过id查找document.getElementsByTagName()//通过标签名document.getElementsByName()//通过name名查找document.getElementsByClassName("类名")//通过类名获取元素对象documen
Karen110 Karen110
2年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Wesley13 Wesley13
2年前
java常用类(2)
三、时间处理相关类Date类:计算机世界把1970年1月1号定为基准时间,每个度量单位是毫秒(1秒的千分之一),用long类型的变量表示时间。Date分配Date对象并初始化对象,以表示自从标准基准时间(称为“历元”(epoch),即1970年1月1日08:00:00GMT)以来的指定毫秒数。示例:packagecn.tanjian
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
2年前
Vue diff 算法
一、虚拟DOM(virtualdom)  diff算法首先要明确一个概念就是diff的对象是虚拟DOM(virtualdom),更新真实DOM是diff算法的结果。  注:virtualdom 可以看作是一个使用JavaScript模拟了DOM结构的树形结构,这个树结构包含
Wesley13 Wesley13
2年前
03. react 初次见面
    与浏览器的DOM元素不同,React当中的元素事实上是普通的对象,ReactDOM可以确保浏览器DOM的数据内容与React元素保持一致。1、将元素渲染到DOM中    首先我们在一个HTML页面中添加一个id"root" 的<div:<divid"root"
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
2个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这