什么是虚拟DOM?

什么是虚拟DOM?,第1张

虚拟DOM是随着时代发展而诞生的产物。在Web早期,页面的交互效果比现在简单得多,没有很复杂的状态需要管理,也不太需要频繁地操作DOM,使用jQuery来开发就可以满足我们的需求。随着时代的发展,页面上的功能越来越多,我们需要实现的需求也越来越复杂,程序中需要维护的状态也越来越多,DOM操作也越来越频繁。

当状态变得越来越多,DOM操作越来越频繁时,我们就会发现如果像之前那样使用jQuery来开发页面,那么代码中会有相当多的代码是在操作DOM,程序中的状态也很难管理,代码中的逻辑也很混乱。这其实是命令式操作DOM的问题,虽然简单易用,但是在业务越来越复杂的今天,它会有不好维护的问题。现在,我们使用的三大主流框架Vue.js、Angular和React都是声明式操作DOM。我们通过描述状态和DOM之间的映射关系是怎样的,就可以将状态渲染成视图。关于状态到视图的转换过程,框架会帮我们做,不需要我们自己手动去操作DOM。

因为浏览器渲染真实dom的时候,根据html和css进行解析-渲染成页面,对于浏览器来说,它会一个一个dom安先后顺序来进行解析渲染,不会进行汇总比较,或者是因为考虑的真实的dom太复杂了,没法进行快速的比较得到最小差异化。

而虚拟dom简单来说就是一开始用js画了一个假的dom结构,映射真实的dom,去除一些我们不用去关注的事情。然后在之后更新dom的时候,先根据这些变更来得出最小的差异化。再把这些差异一次写入真实dom,性能会有很大的提升。

从虚拟dom到真是dom,找到渲染前后真正发生变化的地方,这就是diff算法,考虑到很少进行跨层移动,所以用平层对比,时间复杂度从O(n^3)缩短为O(n),在对比过程中直接对真实dom更新。变更一般有三种: 文本 ,节点属性,节点变更,增删节点(绑定key值的作用)

ps html和css解析成的render树,每一个节点renderObject,浏览器会根据原则

1、它是页面的根对象

2、它具有显式的CSS位置属性(相对、绝对或转换)

3、它是透明的

4、是否有溢出、alpha掩码或反射

5、有一个CSS过滤器

6、对应于具有3D (WebGL)上下文或加速2D上下文的画布元素

7、对应于一个视频元素

来生成renderLayer,每一个renderObject会指向一个renderLayer,直接或者间接指向父节点的renderLayer。当浏览器渲染的时候renderLayer决定了渲染的层级,renderObject决定了渲染的内容

虚拟 DOM ( Virtual DOM )这个概念相信大家都不陌生,从 React 到 Vue ,虚拟 DOM 为这两个框架都带来了跨平台的能力( React-Native 和 Weex )

实际上它只是一层对真实 DOM 的抽象,以 JavaScript 对象 ( VNode 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上

在 Javascript 对象中,虚拟 DOM 表现为一个 Object 对象。并且最少包含标签名 ( tag )、属性 ( attrs ) 和子元素对象 ( children ) 三个属性,不同框架对这三个属性的名命可能会有差别

创建虚拟 DOM 就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟 DOM 对象的节点与真实 DOM 的属性一一照应

在 vue 中同样使用到了虚拟 DOM 技术

定义真实 DOM

实例化 vue

观察 render 的 render ,我们能得到虚拟 DOM

通过 VNode , vue 可以对这颗抽象树进行创建节点,删除节点以及修改节点的操作, 经过 diff 算法得出一些需要修改的最小单位,再更新视图,减少了 dom 操作,提高了性能

DOM 是很慢的,其元素非常庞大,页面的性能问题,大部分都是由 DOM 操作引起的

真实的 DOM 节点,哪怕一个最简单的 div 也包含着很多属性,可以打印出来直观感受一下:

由此可见,操作 DOM 的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验

举个例子:

你用传统的原生 api 或 jQuery 去操作 DOM 时,浏览器会从构建 DOM 树开始从头到尾执行一遍流程

当你在一次操作时,需要更新10个 DOM 节点,浏览器没这么智能,收到第一个更新 DOM 请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程

而通过 VNode ,同样更新10个 DOM 节点,虚拟 DOM 不会立即操作 DOM ,而是将这10次更新的 diff 内容保存到本地的一个 js 对象中,最终将这个 js 对象一次性 attach 到 DOM 树上,避免大量的无谓计算

首先可以看看 vue 中 VNode 的结构

源码位置:src/core/vdom/vnode.js

这里对 VNode 进行稍微的说明:

vue 是通过 createElement 生成 VNode

源码位置:src/core/vdom/create-element.js

上面可以看到 createElement 方法实际上是对 _createElement 方法的封装,对参数的传入进行了判断

可以看到 _createElement 接收5个参数:

根据 normalizationType 的类型, children 会有不同的定义

simpleNormalizeChildren 方法调用场景是 render 函数是编译生成的

normalizeChildren 方法调用场景分为下面两种:

无论是 simpleNormalizeChildren 还是 normalizeChildren 都是对 children 进行规范(使 children 变成了一个类型为 VNode 的 Array ),这里就不展开说了

规范化 children 的源码位置在:src/core/vdom/helpers/normalzie-children.js

在规范化 children 后,就去创建 VNode

createComponent 同样是创建 VNode

源码位置:src/core/vdom/create-component.js

稍微提下 createComponent 生成 VNode 的三个关键流程:

createElement 创建 VNode 的过程,每个 VNode 有 children , children 每个元素也是一个 VNode ,这样就形成了一个虚拟树结构,用于描述真实的 DOM 树结构


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/65097.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-02-28
下一篇2023-02-28

发表评论

登录后才能评论

评论列表(0条)

    保存