内容简介: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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Probability and Computing
Michael Mitzenmacher、Eli Upfal / Cambridge University Press / 2005-01-31 / USD 66.00
Assuming only an elementary background in discrete mathematics, this textbook is an excellent introduction to the probabilistic techniques and paradigms used in the development of probabilistic algori......一起来看看 《Probability and Computing》 这本书的介绍吧!