DOM
- DOM代表了Document Object Model
- DOM是一种抽象化的结构性文本
- DOM的作用只是需要能够直接获取东西
对于Web Developers来说,这种结构性的文本就是HTML code
,并且 DOM被简单的称为HTML DOM。HTML code中的elements
就变成了DOM中的nodes
。
所以,既然HTML是一种文本,那么DOM就是HTML在内存中的表现形式。
将它与程序的一个实例进行比较。你可以在同一个程序的多个过程,就像你可以有多个相同的HTML DOM(例如同一页面加载多标签)。
HTML DOM提供了能够过去和修改Node的API,例如:getElementById
和removeChild
。
我们通常使用JavaScript
来操作DOM对象。
下面举个栗子:
var item = document.getElementById("hei");
item.parentNode.removeChild(item);
document
对象是HTML DOM中root node的一个抽象体。
遇到的问题
由于HTML文档的结构允许,所以HTML DOM使用的是树结构。这就很厉害了,因为通过树结构我们可以很容易地遍历树。不幸的是,很容易操作并不意味着很快。
现在的WEB程序中,DOM树�非常巨大。因为我们越来越推向动态Web应用程序(Single Page Applications - SPAs),我们需要大量的、不断的修改DOM树。�这真的非常痛苦。
顺便说一下,假如我自己设法创造一个5GB +源网页。它其实不是很难。但是考虑一下,我们将要在DOM使成千上万的div。记住,我们是现代的程序猿,我们写出的代码应该很容易管理!我们的方法,处理事件-点击提交,很多,型INS…
比方说,典型的jQuery事件处理程序看起来像这一样:
1、查找对事件感兴趣的每一个节点
2、必要时更新
其中有两个问题:
很难管理。
想象一下,你必须调整一个事件处理程序。如果你失去了背景,你必须潜水真正深入到代码,甚至知道发生了什么事。耗时和错误风险。
它的效率很低。我们真的需要手动做所有这些调查结果吗?也许我们可以更聪明,并提前告诉哪些节点是要更新?
那么Virtual DOM则是为了解决以上这些问题。
Virtual DOM
首先需要讲明的一点
Virtual DOM
技术并不是React发明的,但是React确实是使他发扬光大的。
Virtual DOM是HTML DOM的抽象。它是轻量级和脱离浏览器的具体实施细节。因为DOM本身已经是一个抽象的、虚拟的DOM,事实上,Virtual DOM是一个抽象的抽象。
实际上,Virtual DOM包含:
- Javascript DOM模型树(VTree),类似文档节点树(DOM)
- DOM模型树转节点树方法(VTree -> DOM)
- 两个DOM模型树的差异算法(diff(VTree, VTree) -> PatchObject)
- 根据差异操作节点方法(patch(DOMNode, PatchObject) -> DOMNode)
VTree
VTree模型非常简单,基本结构如下:
{
// tag的名字
tagName: 'p',
// 节点包含属性
properties: { style: { color: '#fff' } },
// 子节点
children: [],
// 该节点的唯一表示,后面会讲有啥用
key: 1
}
所以我们很容易写一个方法来创建这种树状结构,例如React是这么创建的:
// 创建一个divreact.createElement('div', null, [
// 子节点img
react.createElement('img', { src: "avatar.png", class: "profile" }),
// 子节点h3
react.createElement('h3', null, [[user.firstName, user.lastName].join(' ')])
]);
VTree -> DOM
这方法也不太难,我们实现一个简单的:
function create(vds, parent) {
// 首先看看是不是数组,如果不是数组统一成数组
!Array.isArray(vds) && (vds = [vds]);
// 如果没有父元素则创建个fragment来当父元素
parent = parent || document.createDocumentFragment(); var node;
// 遍历所有VNode
vds.forEach(function (vd) {
// 如果VNode是文字节点
if (isText(vd)) {
// 创建文字节点
node = document.createTextNode(vd.text);
// 否则是元素
} else {
// 创建元素
node = document.createElement(vd.tag);
}
// 将元素塞入父容器
parent.appendChild(node);
// 看看有没有子VNode,有孩子则处理孩子
VNode vd.children && vd.children.length && create(vd.children, node);
// 看看有没有属性,有则处理属性
vd.properties && setProps({ style: {} }, vd.properties, node); });
return parent;}
diff(VTree, VTree) -> PatchObject
差异算法是Virtual DOM的核心,实际上该差异算法是个取巧算法(当然你不能指望用O(n^3)的复杂度来解决两个树的差异问题吧),不过能解决Web的大部分问题。
那么React是如何取巧的呢?
- 分层对比
如图,React仅仅对同一层的节点尝试匹配,因为实际上,Web中不太可能把一个Component在不同层中移动。
-
基于key来匹配
还记得之前在VTree中的属性有一个叫key的东东么?这个是一个VNode的唯一识别,用于对两个不同的VTree中的VNode做匹配的。
这也很好理解,因为我们经常会在Web遇到拥有唯一识别的Component(例如课程卡片、用户卡片等等)的不同排列问题。
-
基于自定义元素做优化
React提供自定义元素,所以匹配更加简单。
patch(DOMNode, PatchObject) -> DOMNode
由于diff操作已经找出两个VTree不同的地方,只要根据计算出来的结果,我们就可以对DOM的进行差异渲染。
总结:
- 给定一个可以代表DOM结构的VTree结构
- 通过
vdom/create-element
函数来创建DOM中的节点- 通过将
vdom/patch
函数将vtree/diff
函数根据两个不同的vtree
结构产生的patch
包,更新的DOM中
引用
前沿技术揭秘
The difference between Virtual DOM and DOM