虚拟DOM,到底快在哪
01
—
背景
说起现在比较前沿的前端框架。那你一定知道React、Vue、Angular。
那么这些框架除了一些构造模式、组件化思想、指令语法等方便了我们编程。那你一定还听说过其对外宣称的Virtual DOM。那么今天就让我们一起来探究一下,它究竟快在哪,节省了哪些性能!
02
—
设想
你可以打开脑洞去猜想框架作者的想法。
想做性能的优化,就需要你对浏览器渲染的过程与原理有一定的认知。
从请求结束,解构HTML构建DOM tree,请求CSS styles rule后开始。
到生成Render tree,计算各节点布局信息,各浏览器不同的UI层绘制视觉模型结束。
全局布局是对根节点的重新布局,杜绝大面积的节点重新布局,尽量仅对dirty标记的呈现器做增量布局渲染才是性能问题所在,当然绘制也有增量绘制。
后续会推出一篇文章单独讲解浏览器的渲染机制与优化。
那么此刻我们仅针对渲染树构建,布局和绘制展开如下优化设想。
我们知道频繁的操作DOM是非常耗费性能的,频繁的增删改数据会让浏览器不断的重新排版绘制。
这种操作是同步的,每次都要给浏览器发送指令去全量或增量布局某一片节点树。
试想一下我们是否可以做个对比机制。来记录变动前的DOM,及频繁变动后的最终DOM。对比哪些地方发生了变化,编排出对浏览器性能影响最小的DOM树。
03
—
实践
我们画一个简单的页面。如下:
用浏览器的文档对象document看一下收集的DOM树长什么样子?
我们发现每个子节点实际上就是一个对象,如果展开你可以发现它们是以键值对的形式非常全面的记录了自己的各种属性。
所以,我们可以拿到整个树下任何节点里我们想要的信息。
例如:我们可以用一段代码递归出所有节点的名字
(别的属性也是同理)
打印结果:
代码实现:
function showDom(n, t){
if(Object.prototype.toString.call(n) !== "[object HTMLCollection]") return
n.CONSOLE_TAG = t;
for(let v = 0; v < n.length; v++){
console.info(n.CONSOLE_TAG + n[v].nodeName.toLowerCase())
if(Object.prototype.toString.call(n[v].children) === "[object HTMLCollection]"){
showDom(n[v].children, n.CONSOLE_TAG + "|----")
}
}
}
showDom(document.body.children, "|----")
至此,我们是不是可以自造一个虚拟的DOM树了呢 ?
okay,有了想法,那我们来简单实现一个简陋的 VirtralDom
预期结构:
var VirtralDom = [{
index: id,
tag: 'div',
props: { id: 'App' },
context: 'app',
children: [
index: id,
tag: 'div',
props: { class: 'header' },
context: 'header',
children: []
]
}]
代码实现:
const VirtralDom = [];
function writeVirtralDom(n, a){
if(Object.prototype.toString.call(n) !== "[object HTMLCollection]") return
for(let v = 0; v < n.length; v++){
a.push({
index: v,
tag: n[v].nodeName.toLowerCase(),
props: n[v].attributes,
context: n[v].innerText,
children: []
});
if(Object.prototype.toString.call(n[v].children) === "[object HTMLCollection]"){
writeVirtralDom(n[v].children, a[v].children);
}
}
}
writeVirtralDom(document.body.children, VirtralDom);
console.log(VirtralDom)
打印结果:
可以看到,我们定义的VirtralDom就完成了。
一个小小的数组集结了我们需要的所有DOM节点,我们自造的简陋版VirtralDom就完成了。
当然,一个真实的节点所需的属性远远不止这几个,如果需要,我们也可以将真实节点的属性拷贝到我们的虚拟DOM上。
04
—
结论
每次DOM发生变更时,我们就可以自己记录一个变更前的 oldVirtralDom 及变更后的 newVirtralDom。
如果我们封装个类,记录浏览器1次操作内增删改的N次变化,我们做新老对比,将最后对浏览器性能影响最少的树替换到HTML中定为最终DOM树,让浏览器增量布局是不是就完成了我们的设想呢!!!
至此,我们知道了虚拟DOM的操作原理。实际就是加了中间层运行算法对比类,称其Diff算法,模拟真实DOM的移花接木。
算法的运行所耗是时间资源与计算机内存资源,这部分时间相对于频繁的更新DOM树显然是更快的,那最终都是交由浏览器去布局、绘制。
我将在下一章节,推出 Diff算法的分析,便于更好的理解本章内容,也会系列的推出相关框架的前线面试题解析。
如果觉得本文对你起到了些许作用,那么作为知恩图报的码农,您的 "赞赏" 是我继续前行的动力,点击下方喜欢作者,感谢您的鼓励~
随笔好文与你共度地铁时光