概述
本文通过对virtual-dom的源码进行阅读和分析,针对Virtual DOM的结构和相关的Diff算法进行讲解,让读者能够对整个数据结构以及相关的Diff算法有一定的了解。
Virtual DOM中Diff算法得到的结果如何映射到真实DOM中,我们将在下一篇博客揭晓。
本文的主要内容为:
-
Virtual DOM的结构
-
Virtual DOM的Diff算法
注:这个Virtual DOM的实现并不是React Virtual DOM的源码,而是基于virtual-dom这个库。两者在原理上类似,并且这个库更加简单容易理解。相较于这个库,React对Virtual DOM做了进一步的优化和调整,我会在后续的博客中进行分析。
Virtual DOM的结构
VirtualNode
作为Virtual DOM的元数据结构,VirtualNode位于vnode/vnode.js
文件中。我们截取一部分声明代码来看下内部结构:
function VirtualNode(tagName, properties, children, key, namespace) {
this.tagName = tagName
this.properties = properties || noProperties //props对象,Object类型
this.children = children || noChildren //子节点,Array类型
this.key = key != null ? String(key) : undefined
this.namespace = (typeof namespace === "string") ? namespace : null
...
this.count = count + descendants
this.hasWidgets = hasWidgets
this.hasThunks = hasThunks
this.hooks = hooks
this.descendantHooks = descendantHooks
}
VirtualNode.prototype.version = version //VirtualNode版本号,isVnode()检测标志
VirtualNode.prototype.type = "VirtualNode" // VirtualNode类型,isVnode()检测标志
上面就是一个VirtualNode的完整结构,包含了特定的标签名、属性、子节点等。
VText
VText是一个纯文本的节点,对应的是HTML中的纯文本。因此,这个属性也只有text
这一个字段。
function VirtualText(text) {
this.text = String(text)
}
VirtualText.prototype.version = version
VirtualText.prototype.type = "VirtualText"
VPatch
VPatch是表示需要对Virtual DOM执行的操作记录的数据结构。它位于vnode/vpatch.js
文件中。我们来看下里面的具体代码:
// 定义了操作的常量,如Props变化,增加节点等
VirtualPatch.NONE = 0
VirtualPatch.VTEXT = 1
VirtualPatch.VNODE = 2
VirtualPatch.WIDGET = 3
VirtualPatch.PROPS = 4
VirtualPatch.ORDER = 5
VirtualPatch.INSERT = 6
VirtualPatch.REMOVE = 7
VirtualPatch.THUNK = 8
module.exports = VirtualPatch
function VirtualPatch(type, vNode, patch) {
this.type = Number(type) //操作类型
this.vNode = vNode //需要操作的节点
this.patch = patch //需要操作的内容
}
VirtualPatch.prototype.version = version
VirtualPatch.prototype.type = "VirtualPatch"
其中常量定义了对VNode节点的操作。例如:VTEXT就是增加一个VText节点,PROPS就是当前节点有Props属性改变。
Virtual DOM的Diff算法
了解了虚拟DOM中的三个结构,那我们下面来看下Virtual DOM的Diff算法。
这个Diff算法是Virtual DOM中最核心的一个算法。通过输入初始状态A(VNode)和最终状态B(VNode),这个算法可以得到从A到B的变化步骤(VPatch),根据得到的这一连串步骤,我们就可以知道哪些节点需要新增,哪些节点需要删除,哪些节点的属性有了变化。在这个Diff算法中,又分成了三部分:
-
VNode的Diff算法
-
Props的Diff算法
-
Vnode children的Diff算法
下面,我们就来一个一个介绍这些Diff算法。
VNode的Diff算法
该算法是针对于单个VNode的比较算法。它是用于两个树中单个节点比较的场景。具体算法如下,如果不想直接阅读源码的同学也可以翻到下面,会有相关代码流程说明供大家参考:
function walk(a, b, patch, index) {
if (a === b) {
return
}
var apply = patch[index]
var applyClear = false
if (isThunk(