1. 什么是虚拟 DOM

在 React 中实际上是 render 函数中return 的内容会生成 DOM,return 中的内容由两部分组成,一部分是 JSX ,另一部分就是 state 中的数据,所以简单来讲,在 React 中 JSX 结合 state 就生成了 DOM。

现在抛开虚拟 DOM 不谈,如果让我们去实现 React 中当数据发生变化时如何操作 DOM 来实现页面内容的变化,我们会怎样去实现?

第一种方案:

1)JSX + state 生成真实的 DOM,并显示在页面上

2)state 发生变化

3)此时 JSX + state 再次结合生成新的真实的 DOM

4)新的 DOM 直接替换掉原来的 DOM

这样页面会发生变化,但是生成真实的 DOM 和在页面上再重新加载新的 DOM 都比较耗性能。

第二种方案:

1)JSX + state 生成真实的 DOM,并显示在页面上

2)state 发生变化

3)此时 JSX + state 再次结合生成新的真实的 DOM

4)新的 DOM 和原始的 DOM 作对比,找出差异

5)利用找出的差异,替换掉页面上原始 DOM 的相应部分

此时页面也会发生变化,和方案一相比多了对比步骤但是只需要替换掉原始DOM的一部分即可,综合来说,方案二要优于方案一。

第三种方案

1)JSX + state 生成虚拟 DOM(虚拟 DOM 就是一个 JS 对象,用它来描述真实 DOM)

例如下面这段代码:

<div id='abc'>item</div>

注意上面的 divspan 标签时 JSX 语法,并不是真实的 DOM,这里是先生成虚拟 DOM ,然后再下一步的时候才用虚拟 DOM 生成真实的 DOM,由 JSX 到真实的 DOM 中间有一个虚拟 DOM。

JSX -> 虚拟DOM(JS对象) -> 真实DOM

也就是说,JSX 需要先转换为 JS 对象,然后再转换为真实的 DOM。

生成的虚拟 DOM 为

['div',{id: 'abc'}, 'item']

虚拟 DOM 的格式为

['标签名',标签属性对象,子标签]

那么 <div id='abc'>item</div> 是如何转化为 JS 对象的呢?

实际上在 React 中上面这样写就相当于下面这样写:

React.createElement('div', {id: 'abc'}, 'item');

那么实际上就算是没有 JSX 语法通过上面这样写也是可以的,但是会非常不方便。

2)用虚拟 DOM 的结构生成真实的 DOM 显示在页面上。

3)JSX + state 生成新的虚拟 DOM

4)两个虚拟 DOM 进行对比,找出差异

5)根据差异直接修改替换页面上的 DOM

虚拟 DOM 是一个 JS 对象,生成一个虚拟 DOM 比生成一个真实的 DOM 结构要容易省时地多,而且两个虚拟 DOM(JS 对象) 之间的对比也比较简单,所以方案三最佳。

React 中使用的也是第三种方案的思想。

2. 虚拟 DOM 的优点

那么虚拟DOM的优点到底有哪些呢?

1)性能提升

这一点通过上面的比较就可以看得出来

2)使得跨端应用得以实现,例如原生应用。

React Native 能够做原生应用虚拟 DOM 是很重要的一方面,原生应用中是没有 DOM 这个概念的,DOM 是浏览器中存在的,但是有了虚拟 DOM(JS 对象) 之后,在原生应用中就可以将虚拟 DOM(JS 对象) 转换为一些原生应用中能够支持的原生组件在原生应用中显示。

3. 虚拟 DOM 的对比

使用虚拟 DOM 时很重要的一个步骤就是两个虚拟 DOM 之间的比较,那么怎样去进行比较呢?

React 中采用 diff 算法,简单来说主要有以下三个方面:

1)当短时间内连续调用多次 setState 时,React 只会进行一次虚拟 DOM 的比对。

我们知道当 state 或者 props 发生变化时,页面会发生变化,实际上 props 的变化也是因为父组件 state 的变化,所以当页面发生变化时实际上是调用 setState 导致数据发生变化变化时。当短时间内连续调用多次 setState 时,如果每次都进行一次虚拟 DOM 的比对,那么性能会比较低,反之多次调用 setState 只进行一次虚拟 DOM 的比对会提升性能。这也是为什么 setState 要设置成异步的原因,因为如果同步的话当执行完一次 setState 时就会发生一次虚拟 DOM 的比对。(同步是顺序立即执行,异步是当所有的同步程序执行完后再执行)

2)在比较虚拟 DOM 时采用逐层同层比较,当上一层出现差异时,那么下面的各层就不需要再比较了,下面各层的 DOM 都将被新的 DOM 替换。

这样做看起来,复用性不是很好,因为下面各层有可能会有许多相同的 DOM。但是这样做会使得比较算法非常简单,比较的速度非常快。

3)设置 key 值

假设现在有一个数组 [a, b, c] 遍历每一项显示在页面上,现在数组发生变化将第一项 a 删掉,如果没有 key 值,数组 [b, c] 无法和原数组进行比对,例如 b 到底和原数组的哪一个进行比较呢?

但是现在假设有了 key 值,原数组中 a 的 key 值是 a,b 的 key 值是 b,c 的 key 值是 c。删除 a 之后,通过 key 值,b 的 key 值 b 在原数组中找到 b,说明 b 没有发生变化,c 同理也没有发生变化,但是原数组中的 a 在新数组中并没有找到,说明新数组中将 a 删掉了,所以在操作页面时将 a 删掉即可。

这里有一点需要注意的是,key 值一定要选不能变化的,利用数组的索引来做 key 值就不可取。还是以上面为例进行说明。原数组的 a 的 key 值是 0,b 的 key 值是 1,c 的 key 值是 2,删掉 a 后,新数组的 b 的 key 值是 0,c 的 key 值是 1,经过比对原数组的 a 和新数组的 b key 值相同,虚拟 DOM 会认为它们是相同的,没有差异,但是实际上它们是不同的。

聊一聊React中虚拟DOM的更多相关文章

  1. react中虚拟dom的diff算法

    .state 数据 .jsx模板 .生成虚拟dom(虚拟DOM就是一个js对象,用它来描述真实DOM) ['div', {id:'abc'}, ['span', {}, 'hello world']] ...

  2. react中虚拟DOM的基本概念

    react中的核心概念 1.DOM的本质是什么: 浏览器中的概念,用js对象来表示页面上的元素,并提供操作DOM对象的API 2.什么事react中的虚拟DOM:是框架中的概念,是程序员用js对象来模 ...

  3. react中虚拟DOM

    简单来说虚拟DOM就是一个js对象,相对于真实dom来做比较更节约性能,虚拟DOM执行过程如下

  4. 简单实现react中虚拟DOM渲染

    /** * @method createElement * @param type {string} * @param props {Object} * @param children {string ...

  5. 【React自制全家桶】二、分析React的虚拟DOM和Diff算法

    一.React如何更新DOM内容: 1.  获取state 数据 2.  获取JSX模版 3.  通过数据 +模版结合,生成真实的DOM, 来显示,以下行代码为例(简称代码1) <div id= ...

  6. React的虚拟DOM

    ReactJs的一大特点就是引进了虚拟dom(Virtual DOM)的概念.为什么我们需要Virtual DOM,Virtual DOM给我们带来了什么优势. 首先我们要了解一下浏览器的工作流. 当 ...

  7. React之虚拟DOM中的Diff算法

    一.React中的setState ( 异步函数,异步获取数据 ) 若操作的时间间隔短,它可以将多个setState结合成一个setState,减少虚拟DOM的比对次数,提高性能 二.同层虚拟DOM对 ...

  8. JavaScript是如何工作的:编写自己的Web开发框架 + React及其虚拟DOM原理

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 19 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  9. react 的虚拟dom

    前端优化的主要方面就是减少页面的DOM操作,减少重排和重绘,React在这方面做了优化,采用了所谓的虚拟DOM,其实我们平时也会遇到虚拟DOM,只是你没有注意罢了,请听我娓娓道来.  所谓的虚拟DOM ...

随机推荐

  1. AD复制问题汇总

    1:文件复制服务NtFrs 13568报错的解决方法 解决方法: 建议不要按照日志的提示进行操作,正确的操作应该是 出现这个问题的原因,是由于在硬件的损坏,导致服务器未正确处理NTFS USN 日志. ...

  2. 吴裕雄--天生自然python学习笔记:Python3 标准库概览

    操作系统接口 os模块提供了不少与操作系统相关联的函数. >>> import os >>> os.getcwd() # 返回当前的工作目录 'C:\\Python ...

  3. 题解:BZOJ 1009 HNOI2008 GT考试 KMP + 矩阵

    原题描述: 阿申准备报名参加GT考试,准考证号为N位数 X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字.他的不吉利数学A1A2...Am(0<=Ai&a ...

  4. Java 内部类(成员内部类、局部内部类、静态内部类,匿名内部类)

    一.什么是内部类? 内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用publ ...

  5. C++二级指针和指针引用传参

    前提 一级指针和引用 已经清晰一级指针和引用. 可参考:指针和引用与及指针常量和常量指针 或查阅其他资料. 一级指针和二级指针 个人觉得文字描述比较难读懂,直接看代码运行结果分析好些,如果想看文字分析 ...

  6. @RequestBody 参数为string正常改为对象时不报错但获取不到值

    @RequestBody 参数为string正常改为对象时不报错但获取不到值 试了好多办法都不行 最后 原因  jackson 包的版本号不匹配 2.9.0的不能封装进对象,可以运行且不报错但获取不到 ...

  7. google搜索运算符+101个Google技巧 - Google技巧的终极收集

    下面内容整理自网络 搜索运算符 如果您使用我们的基本搜索技巧后,并未找到想要搜索的内容,可以尝试使用搜索运算符.您只需在 Google 搜索框中将这些符号或字词添加到搜索字词中,即可更好地掌控要显示的 ...

  8. iPhone X会成为苹果最短命的旗舰机型吗?

    最近,有媒体报道有凯基证券分析师郭明琪在他的最新报告指出,iPhone X将在今年中结束生产.因为苹果已计划下半年推出新款iPhone,价格也比iPhone X会低并有新功能发布.所以他预计iPhon ...

  9. 同时安装Python2,Python3如何解决冲突问题【官方解法】

    使用py -2或py -3命令来区分调用Python启动器 去掉参数 -2/-3如何运行? 每次运行都要加入参数-2/-3还是比较麻烦,所以py.exe这个启动器允许你在代码中加入说明,表明这个文件应 ...

  10. Proto3:编码

    本文介绍protocol buffer消息二进制传输格式.在应用程序中使用protocol buffer时,你并不需要了解这些,但它对你了解protocol buffer格式如何影响你的编码消息的大小 ...