一、前言

现在web前端的开发,对于MVVM框架的运用,那是信手拈来,用的飞起。一个xxx-cli工具,就能初始化一套模板,再填充业务代码,打包部署即可。但是会用,是一个方面,大家有没有底层深入思考一下,这些框架核心的技术突破点在哪里?解决了哪些问题?作为一个爱主动学习的童鞋,我们得花点小时间稍微去研究一下才行。今天,我们就简单谈谈虚拟dom,来揭开它的神秘面纱。

首先看一道经典的面试题:

“为什么我们需要虚拟 DOM?”。

这个问题比较常见的回答思路是:“DOM 操作是很慢的,而 JS 却可以很快,直接操作 DOM 可能会导致频繁的回流与重绘,JS 不存在这些问题。因此虚拟 DOM 比原生 DOM 更快”

但实际真实这样吗?

二、虚拟dom是什么?what。

虚拟 DOM(Virtual DOM)本质上是JS 和 DOM 之间的一个映射缓存,它在形态上表现为一个能够描述 DOM 结构及其属性信息的 JS 对象。它主要存储在内存中。主要来说:

  • 1.虚拟dom是一个js对象
  • 2.虚拟dom能够描述真实dom(存在一个对应关系)
  • 3.存储在内存之中

以react为例,我们来看看虚拟dom到底长什么样子: 

三、为什么会有虚拟dom?why。

首先,我们来看看远古时代(可能就是在7,8年前),我们怎么操作dom的。

1、什么都不用,原生js

来看看原生js下我们怎么查找元素的

// 根据类名查询元素,返回一个满足查询条件数组
document.getElementsByClassName() // 根据标签名查询元素,返回一个满足查询条件数组
document.getElementsByTagName() // 根据标签名查询元素,返回一个满足查询条件结果
document.getElementById() .....

来看看我们怎么动态修改dom的

var displayName = 'hello,admin';
document.getElementById('#id').innerHtml = '<div>' + displayName+'</div>'; window.document.body.append('<div>hahahha</div>');

大家觉得这么操作dom会高效吗?

2、jquery时代

jQuery在操作dom时,对DOM API 封装为了相对简单和优雅的形式,同时一口气做掉了跨浏览器的兼容工作,并且提供了链式 API 调用、插件扩展等一系列能力用于进一步解放生产力。最终达到的效果正是我们喜闻乐见的“写得更少,做得更多”。

jQuery 使 DOM 操作变得简单、快速,并且始终确保其形式稳定、可用性稳定。虽然现在看来并不完美,但是当年能够一统江湖,确实在解放生产力,提高效率方面有极大的促进作用。

$('#id').show();
$('.className').hide().html('xxxxx');
3、模板引擎

我们以Handlebars这个模板为例

<!-- Include Handlebars from a CDN -->
<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
<script>
// compile the template
var template = Handlebars.compile("Handlebars <b>{{doesWhat}}</b>");
// execute the compiled template and print the output to the console
console.log(template({ doesWhat: "rocks!" })); </script>
{
people: [
"Yehuda Katz",
"Alan Johnson",
"Charles Jolley",
],
}
 <ul class="people_list">
{{#each people}}
<li>{{this}}</li>
{{/each}}
</ul>

用模板引擎,来操作js,效率还是挺高的,爽歪歪啊。 每次数据发生变化时,我们都不用关心到底是哪里的数据变了,也不用手动去点对点完成 DOM 的修改。只需要关注的仅仅是数据和数据变化本身,DOM 层面的改变模板引擎会帮我们做掉。

类似这种模板引擎,还有很多,我们可以对比一下其性能是怎样的: 

模板引擎像极了一个只需要接收命令,就能够把活干得漂漂亮亮的“扫地机器人”!可惜的是,模板引擎出现的契机虽然是为了使用户界面与业务数据相分离,但实际的应用场景基本局限在实现高效的字符串拼接这一个点上,因此不能指望它去做太复杂的事情。尤其令人无法接受的是,它在性能上的表现并不尽如人意:由于不够“智能”,它更新 DOM 的方式是将已经渲染出 DOM 整体注销后再整体重渲染,并且不存在更新缓冲这一说。在 DOM 操作频繁的场景下,模板引擎可能会直接导致页面卡死。

也就是说,其实我们想要高效迅速的操作dom,同时还保证一个不错的性能。修改了局部dom就替换局部。而不是整体全部替换。但是如果一直都是操作真实的dom,一直都是存在DOM 整体注销后再整体重渲染的情况,那性能效率方面就大打折扣了。----react团队想了想,我们操作假的dom不就行了吗?

因此 虚拟dom就自然而然的应运而生。

三、虚拟dom怎本工作的?how。

借用大神的一张图来说明:

由此图我们可以得出:

数据 + 模板 => 虚拟dom

这里的模板,不一定是我们之前讲的Handlebars这样的js模板引擎。在react中,这种模板是以jsx的形势存在。jsx 是Javascript 的一种语法扩展。你可以理解为在中react使用体验和模板相似的 JS 语法糖。

相比于之前传统的修改dom的方式,这种修改就是多了一个缓冲区。当 DOM 操作(渲染更新)比较频繁时,它会先将前后两次的虚拟 DOM 树进行对比,定位出具体需要更新的部分,生成一个“补丁集”,最后只把“补丁”打在需要更新的那部分真实 DOM 上,实现精准的“差量更新”。 

需要注意的是,我们这里为了方便讲解虚拟dom,会借助react举例来说明,实际上,vue也支持虚拟dom。也就是说虚拟dom,是不依赖框架的。但是 react 主推虚拟dom,想更深入的掌握react,必须要了解虚拟dom。

在整个 DOM 操作的演化过程中,主要矛盾并不在于性能,在于研发体验/研发效率(在于开发者写得爽不爽)。虚拟 DOM 不是别的,正是前端开发们为了追求更好的研发体验和研发效率而创造出来的高阶产物。

虚拟 DOM 并不一定会带来更好的性能,React 官方也从来没有把虚拟 DOM 作为性能层面的卖点对外输出过。虚拟 DOM 的优越之处在于,它能够在提供更爽、更高效的研发模式(也就是函数式的 UI 编程方式)的同时,仍然保持一个还不错的性能。

四、性能浅析对比

  • 动态生成 HTML 字符串的过程本质是对字符串的拼接,对性能的消耗是有限的;而虚拟 DOM 的构建和 diff 过程逻辑则相对复杂,它不可避免地涉及递归、遍历等耗时操作。因此在 JS 行为这个层面,模板渲染胜出。

  • 模板和虚拟 DOM 在最后更新dom阶段,都属于 DOM 范畴的行为,两者具备可比性,因此我们仍然可以愉快地对比下去:模板渲染是全量更新,而虚拟 DOM 是差量更新。

一般来说,更新一定比全量更新高效,但你需要考虑这样一种情况:数据内容变化非常大(或者说整个发生了改变),促使差量更新计算出来的结果和全量更新极为接近(或者说完全一样)。那么虚拟dom在更新dom阶段没有性能优势可言。

在这种情况下,DOM 更新的工作量基本一致,而虚拟 DOM 却伴随着开销更大的 JS 计算,此时会出现的一种现象就是模板渲染和虚拟 DOM 在整体性能上难分伯仲:若两者最终计算出的 DOM 更新内容完全一致,那么虚拟 DOM 大概率不敌模板渲染;但只要两者在最终 DOM 操作量上拉开那么一点点的差距,虚拟 DOM 就将具备战胜模板渲染的底气。因为虚拟 DOM 的劣势主要在于 JS 计算的耗时,而 DOM 操作的能耗和 JS 计算的能耗根本不在一个量级,极少量的 DOM 操作耗费的性能足以支撑大量的 JS 计算。

在实际的开发中,更加高频的场景是这样的:我每次 setState 的时候只修改少量的数据,比如一个对象中的某几个属性,再比如一个数组中的某几个元素。在这样的场景下,模板渲染和虚拟 DOM 之间 DOM 操作量级的差距就完全拉开了,虚拟 DOM 将在性能上具备绝对的优势。

同时,我们考虑这种情况:短时间内对3种状态进行修改,有虚拟dom这个缓冲,react可以将其加入到批量更新的任务中去。而不是每次不停的立即更新真是dom。

五、虚拟dom的意义

回到最开始的问题:为什么我们需要虚拟dom?

在整个 DOM 操作的演化过程中,主要矛盾并不在于性能,而在于开发者写得爽不爽,在于研发体验/研发效率。虚拟 DOM 不是别的,正是前端开发们为了追求更好的研发体验和研发效率而创造出来的高阶产物。虚拟 DOM 并不一定会带来更好的性能,React 官方也从来没有把虚拟 DOM 作为性能层面的卖点对外输出过。虚拟 DOM 的优越之处在于,它能够在提供更爽、更高效的研发模式(也就是函数式的 UI 编程方式)的同时,仍然保持一个还不错的性能。

虚拟 DOM 解决的关键问题有以下两个。

  • 1、研发体验/研发效率的问题:这一点前面已经反复强调过,DOM 操作模式的每一次革新,背后都是前端对效率和体验的进一步追求。虚拟 DOM 的出现,为数据驱动视图这一思想提供了高度可用的载体,使得前端开发能够基于函数式 UI 的编程方式实现高效的声明式编程。

  • 2、跨平台的问题:虚拟 DOM 是对真实渲染内容的一层抽象。若没有这一层抽象,那么视图层将和渲染平台紧密耦合在一起,为了描述同样的视图内容,你可能要分别在 Web 端和 Native 端写完全不同的两套甚至多套代码。但现在中间多了一层描述性的虚拟 DOM,它描述的东西可以是真实 DOM,也可以是iOS 界面、安卓界面、小程序......同一套虚拟 DOM,可以对接不同平台的渲染逻辑,从而实现“一次编码,多端运行”,

归根结底,解决跨平台的问题,也是提高研发效率。

怎么理解虚拟 DOM?的更多相关文章

  1. 全面理解虚拟DOM(1)

    最近一两年前端最火的技术莫过于 reactjs,angularJS,vuejs,即便你没用过也可能听过,像ReactJS由业界顶尖的互联网公司facebook提出,其本身有很多先进的设计思路,比如页面 ...

  2. 全面理解虚拟DOM,实现虚拟DOM

    1.为什么需要虚拟DOM DOM是很慢的,其元素非常庞大,页面的性能问题鲜有由JS引起的,大部分都是由DOM操作引起的.如果对前端工作进行抽象的话,主要就是维护状态和更新视图:而更新视图和维护状态都需 ...

  3. 手写一个虚拟DOM库,彻底让你理解diff算法

    所谓虚拟DOM就是用js对象来描述真实DOM,它相对于原生DOM更加轻量,因为真正的DOM对象附带有非常多的属性,另外配合虚拟DOM的diff算法,能以最少的操作来更新DOM,除此之外,也能让Vue和 ...

  4. 虚拟dom与diff算法 分析

    好文集合: 深入浅出React(四):虚拟DOM Diff算法解析 全面理解虚拟DOM,实现虚拟DOM

  5. 什么是虚拟DOM?

    (摘抄自一篇文章,觉得这里写得非常不错,所以单独放出来,希望能对大家有帮助.)React为啥这么大?因为它实现了一个虚拟DOM(Virtual DOM).虚拟DOM是干什么的?这就要从浏览器本身讲起 ...

  6. 如何编写自己的虚拟DOM

    要构建自己的虚拟DOM,需要知道两件事.你甚至不需要深入 React 的源代码或者深入任何其他虚拟DOM实现的源代码,因为它们是如此庞大和复杂--但实际上,虚拟DOM的主要部分只需不到50行代码. 有 ...

  7. [react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?

    壹 ❀ 引 虚拟DOM(Virtual DOM)在前端领域也算是老生常谈的话题了,若你了解过vue或者react一定避不开这个话题,因此虚拟DOM也算是面试中常问的一个点,那么通过本文,你将了解到如下 ...

  8. Virtual DOM 虚拟DOM的理解(转)

    作者:戴嘉华 转载请注明出处并保留原文链接( #13 )和作者信息. 目录: 1 前言 2 对前端应用状态管理思考 3 Virtual DOM 算法 4 算法实现 4.1 步骤一:用JS对象模拟DOM ...

  9. 深刻理解 React (一) ——JSX和虚拟DOM

    版权声明:本文由左明原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/155 来源:腾云阁 https://www.qclou ...

随机推荐

  1. 理解import声明 与 export声明

    import的两种使用方法 import "mod"; // 引入一个模块 import v from "mod"; // 把模块默认的导出值放入变量 v im ...

  2. spring与缓存注解,以及encache缓存使用

    随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一.Spring 3开始提供了强大的基于注解的缓 ...

  3. D. Concatenated Multiples 解析(思維)

    Codeforce 1029 D. Concatenated Multiples 解析(思維) 今天我們來看看CF1029D 題目連結 題目 給你一個序列\(a\)和一個數字\(k\),求有幾種ind ...

  4. D. Equalize the Remainders 解析(思維)

    Codeforce 999 D. Equalize the Remainders 解析(思維) 今天我們來看看CF999D 題目連結 題目 略,請直接看原題 前言 感覺要搞個類似\(stack\)的東 ...

  5. 循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)

    在我们使用Vue+Element开发前端的时候,往往涉及到很多界面组件的使用,其中很多直接采用Element官方的案例即可,有些则是在这个基础上封装更好利用.更少代码的组件:另外有些则是直接采用第三方 ...

  6. 做Java开发7年,今年9个月时间圆梦饿了么P7

    前言 工作拧螺丝,面试造火箭.我想这是每个程序员比较头疼的事情吧!但是,又必须要经历一个面试流程,尤其是摸不清面试官问的问题,导致面试的时候不知道如何回答.本文是工作7年Java程序员从几十次面试中挑 ...

  7. Java中的有关日期的对象

    目录 Date SimpleDateFormat Calendar 常用方法 Java8中用于计算时差的类 1. Period(以年月日来表示日期差) 2. Duration(以秒与毫秒来表示时间差) ...

  8. Dapr Java Http 调用

    版本介绍 Java 版本:8 Dapr Java SKD 版本:0.9.2 Dapr Java-SDK HTTP 调用文档 有个先决条件,内容如下: Dapr and Dapr CLI. Java J ...

  9. Android Studio导入github项目源码步骤

    1.从github上将源码下载下来 2.打开AS,新建一个新项目(我选择了EmptyActivity) 3.先不要在AS 中打开源码,来整理源码 在源码的目录下面,将project下的build.gr ...

  10. Hadoop调优 | NameNode主备宕机引发的思考

    大家都知道在双十一这些电商大型营销活动期间,电商网站的访问量等是平时的N倍.每当这个时候到来,无论是开发还是运维人员都严阵以待生怕服务出现问题.很不幸,笔者的一个朋友在一家电商公司上班,在双十一时,恰 ...