js原生是否有虚拟dom

JavaScript015

js原生是否有虚拟dom,第1张

js原生是有虚拟dom地,构建出js虚拟DOM树的代码是:

// 构造虚拟DOM对象类function Element(tagName, props, children){this.tagName=tagName

this.props = props。

this.children = children}// 创建虚拟DOMfunction createElement(tagName, props, children) {

return new Element(tagName, props, children)}//  <ul class="ul-wrap">// 。

<li class="li-item">1</li>//         <li class="li-item">2</li>//         <li class="li-item">3</li>//。

</ul>// 假设我们有如上的DOM结构, 那我我们就可以利用虚拟DOM模拟出一个类似的DOM树结构。

let VDOM = createElement("ul", {class: "ul-wrap",}, [createElement("li", {class: "li-item"

}, ["1"]), createElement("li", {class: "li-item"}, ["2"])。

createElement("li", { class: "li-item"}, ["3"]),])console.dir(JSON.stringify(VDOM,null,2))。

我们可以在控制在中打印出打印的虚拟DOM为以下结构。

使用virtual DOM算法的步骤是:

1、用原生JavaScript对象结构模拟出DOM树结构,利用这个树构建一个真正的DOM树,并渲染到页面中。

2、当状态变更时,重新构建一个新的虚拟DOM树,然后用新的树和旧的树进行对比,记录并保存出两棵树的差异。

3、当步骤二记录的差异应用到步骤一中所构建的真正的DOM树上,视图就更新了。

虚拟 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 树结构

1. 虚拟DOM其实就是一个普通的JS对象,是为了提高页面渲染的性能。

虚拟DOM的解决方式是,通过状态生产一个虚拟状态Dom,然后根据虚拟节点进行渲染,假如是首次渲染的就会直接渲染,但是二次往后的话就是进行虚拟状态树的对比,只更新不同的地方。

2. 换成AST的是Vue模板,Vue需要根据模版去处理各种插值、指令;

生成虚拟DOM的是最终要展示在页面上的内容的对象描述,Vue每次需要通过Diff算法对比新旧虚拟DOM的差异;

固定模版生成的AST是不变的,虚拟DOM是不断变化、需要进行差异对比的(数据等会变)。

  template -->抽象语法树 -->render(h) -->虚拟DOM-->UI

3. 抽象语法树的终点是渲染函数(h函数)。

渲染函数(h函数),它既是AST的产物,也是vnode(虚拟节点)的起源。h函数里面是不含指令的。

抽象语法树不会进行diff算法的并且抽象语法树不会直接生成虚拟节点,抽象语法树最终生成的是渲染函数的

参考 https://zhuanlan.zhihu.com/p/111715881