内容简介:Vue在2.0版本引入了虚拟DOM。其了虚拟DOM算法是基于snabbdom算法所做的修改。参看用JavaScript模拟DOM树形成虚拟DOM树,如下面的html结构可以使用如下JS表示
Vue在2.0版本引入了虚拟DOM。其了虚拟DOM算法是基于snabbdom算法所做的修改。参看 github.com/vuejs/vue/b… 注释部分。要想了解Vue,必须了解虚拟DOM,本篇文章主要介绍了什么是虚拟DOM,为什么用虚拟DOM以及其实现节点
一、什么是虚拟DOM
用JavaScript模拟DOM树形成虚拟DOM树,如下面的html结构
<ul style="color:#000"> <li>苹果</li> <li>香蕉</li> <li>橙子</li> </ul> 复制代码
可以使用如下JS表示
{ sel: 'ul', data: { style: {color: '#000'}}, // 节点属性及绑定事件等 children: [ // 子节点 {sel: 'li', text: '苹果'}, {sel: 'li', text: '香蕉'}, {sel: 'li', text: '橙子'} ] } 复制代码
二、为什么要用虚拟DOM
因为对DOM的直接操作是非常慢而且低效的。浏览器的渲染流程包括解析html以构建dom树->构建render树->布局render树->绘制render树,而每一次DOM改变从构建render树到布局到渲染都要重来。参考文档
而虚拟DOM的优势就是:1.开发者不再关心DOM而只关心数据,提升开发效率。2.保证最小化的DOM操作,使执行效率得到提升。
虚拟DOM的优势并不在于它操作DOM比较快,而是能够通过虚拟DOM的比较,最小化真实DOM操作,参考文档
三、虚拟DOM的实现
实现虚拟DOM包含以下三个步骤
-
用JavaScript模拟DOM树形成虚拟DOM树
-
当组件状态发生更新时,比较新旧虚拟DOM树
-
将差异应用到真正的DOM上
3.1 用JavaScript模拟DOM树形成虚拟DOM树
虚拟DOM对象包含以下属性:
- sel:选择器
- data:绑定的数据(attribute/props/eventlistner/class/dataset/hook)
- children:子节点数组
- text:当前text节点内容
- elm: 对真实dom element的引用
- key:用于优化DOM操作
3.2 当组件状态发生更新时,比较新旧虚拟DOM树
给定任意两棵树,找到最少的转换步骤。但是标准的的Diff算法复杂度需要O(n^3).
这显然无法满足性能的要求,考虑到前端操作的情况--我们很少跨级别的修改节点,通常是修改节点的属性、调整子节点的顺序、添加子节点等。当比较虚拟DOM树的时候,如果发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个DOM树的比较。
虚拟DOM在比较时只比较同层次节点,其复杂度降低到了O(n). 而且比较时只比较其key和sel是否相同,相同即为相同节点。
function sameVnode(vnode1: VNode, vnode2: VNode): boolean { return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel; } 复制代码
例子:下图节点从左图变为右图
虚拟DOM的做法是
A.destroy(); A = new A(); A.append(new B()); A.append(new C()); D.append(A); 复制代码
而不是
A.parent.remove(A); D.append(A); 复制代码
3.3 将差异应用到真正的DOM上
- 如果旧节点不在,则将新节点插入
- 如果新节点不存在,则将旧节点删除
- 如果新旧相同(key和sel相同):
function patchVnode(oldVnode: VNode, vnode: VNode, insertedVnodeQueue: VNodeQueue) { ... const elm = vnode.elm = (oldVnode.elm as Node); let oldCh = oldVnode.children; let ch = vnode.children; if (oldVnode === vnode) return; // 都是undefined ... if (isUndef(vnode.text)) { // 新节点不是textNode if (isDef(oldCh) && isDef(ch)) { // 子节点都存在,updateChildren对子节点进行diff if (oldCh !== ch) updateChildren(elm, oldCh as Array<VNode>, ch as Array<VNode>, insertedVnodeQueue); } else if (isDef(ch)) { // 旧节点没有子节点,且新节点有子节点。将新节点的子节点添加进来 if (isDef(oldVnode.text)) api.setTextContent(elm, ''); addVnodes(elm, null, ch as Array<VNode>, 0, (ch as Array<VNode>).length - 1, insertedVnodeQueue); } else if (isDef(oldCh)) { // 新节点没有子节点,且旧节点有子节点。 删除旧节点的子节点 removeVnodes(elm, oldCh as Array<VNode>, 0, (oldCh as Array<VNode>).length - 1); } else if (isDef(oldVnode.text)) { // 新旧节点都没有子节点。更新text api.setTextContent(elm, ''); } } else if (oldVnode.text !== vnode.text) { // 新节点是textNode且新旧不一致 api.setTextContent(elm, vnode.text as string); } ... } 复制代码
四、举个例子
如果两个元素相同(key和sel),则判断其children,过程中维护四个变量
- oldStartIdx => 旧头索引
- oldEndIdx => 旧尾索引
- newStartIdx => 新头索引
- newEndIdx => 新尾索引
例如下图中children由ABCDEF -> ADGCEF, 其中假设其sel相同且都设置有key ,A的key为A,B的key为B,依次类推
循环判断如下:
- step1:比较首元素(oldStart/newStart),相同则后移继续,否则step2
- step2:比较尾元素(oldEnd/newEnd) ,相同则前移继续,否则step3
- step3:比较首尾元素(oldStart/newEnd) ,相同则移动元素并继续,否则step4
- step4:比较尾首元素(oldEnd/newStart) ,相同则移动元素并继续,否则step5
- step5:判断newStart在旧节点中是否存在,存在则移动,否则新增
- 最后:删除多余的旧节点或插入多余的新节点
为什么维护四个变量?有什么优势?两个变量是否可以?此处留个疑问。
第一步
oldStart === newStart,则执行上面3.3. 且oldStartIdx++, newStartIdx++.
第二步
oldEnd === newEnd,则执行上面3.3. 且oldEndIdx--, newEndIdx--.
第三步
同上,oldEnd === newEnd,则执行上面3.3. 且oldEndIdx--, newEndIdx--.
第四步
- oldStart !== newStart
- oldEnd !== newEnd
- oldStart !== newEnd
- oldEnd === newStart
oldEnd === newStart,将oldEnd插入到oldStart之前,并执行上面3.3. 且oldEndIdx--, newStartIdx++.
第五步
首尾元素均不相同!判断newStart在旧元素中是否存在,存在则移动,否则将新元素插入
oldKeyToIdx = [B, C] // 从oldStartIdx到oldEndIdx的所有元素 G in [B, C] ? NO! 复制代码
将newStart插入到oldStart之前,并执行上面3.3.且newStartIdx++.
第六步
同上。H in [B, C] ? NO! 将newStart插入到oldStart之前,并执行3.3.且newStartIdx++.
以上所述就是小编给大家介绍的《了解虚拟DOM》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 了解虚拟化硬件支持
- 是时候深入了解JAVA虚拟机了!
- 一文了解Java虚拟机的重要组成
- 了解一下,Android 10中的ART虚拟机(8)
- 是时候了解一波虚拟机的类加载机制
- 三分钟了解到底虚拟化平台能做什么?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
算法导论(原书第2版)
[美] Thomas H.Cormen、Charles E.Leiserson、Ronald L.Rivest、Clifford Stein / 潘金贵 等 / 机械工业出版社 / 2006-9 / 85.00元
这本书深入浅出,全面地介绍了计算机算法。对每一个算法的分析既易于理解又十分有趣,并保持了数学严谨性。本书的设计目标全面,适用于多种用途。涵盖的内容有:算法在计算中的作用,概率分析和随机算法的介绍。书中专门讨论了线性规划,介绍了动态规划的两个应用,随机化和线性规划技术的近似算法等,还有有关递归求解、快速排序中用到的划分方法与期望线性时间顺序统计算法,以及对贪心算法元素的讨论。此书还介绍了对强连通子图......一起来看看 《算法导论(原书第2版)》 这本书的介绍吧!