一、虚拟DOM
虚拟Dom,就是一个用来描述真实Dom的对象,它有六个属性:
sel表示当前节点标签名data内是节点的属性children表示当前节点的其他子标签节点elm表示当前虚拟节点对应的真实节点(这里暂时没有)key即为当前节点的keytext表示当前节点下的文本
其结构如下:
let vnode = {
sel: 'ul',
data: {},
children: [
{
sel: 'li', data: { class: 'item' }, text: 'son1'
},
{
sel: 'li', data: { class: 'item' }, text: 'son2'
},
],
elm: undefined,
key: undefined,
text: undefined
}
虚拟Dom可以理解成真实Dom的一种状态。当真实Dom变化后,虚拟Dom可以提供一个真实Dom变化前后的状态,通过对比两个状态,可以得出真实Dom需要更新的部分,可实现最小量更新。在一些复杂的Dom变化场景中,通过对比虚拟Dom后更新真实Dom会比直接更新真实Dom的效率高,这也就是虚拟Dom和diff算法真正存在的意义。
二、h函数
h函数是用于构造虚拟Dom的函数,h函数可以接受多种类型的参数,并在内部执行vnode函数。根据传入h函数来决定执行vnode函数传入的参数。vnode函数的作用就是将传入的参数转换为一个对象,也就是虚拟Dom。
// vnode.js
export default function (sel, data, children, text, elm) {
const key = data.key
return {sel, data, children, text, elm, key}
}
三、diff对比规则
// 第一个参数是sel 第二个参数是data 第三个参数是children
const myVnode1 = h("h1", {}, [
h("p", {key: "a"}, "a"),
h("p", {key: "b"}, "b"),
]);
const myVnode2 = h("h1", {}, [
h("p", {key: "c"}, "c"),
h("p", {key: "d"}, "d"),
]);
3.1 patch
比较的第一步在于执行patch,它相当于对比的入口。patch的主要作用是对比两个虚拟Dom的根节点,并根据对比结果操作真实Dom。
首先对比节点本身,判断是否为同一节点,如果不为相同节点,则删除节点重新创建节点进行替换。
3.2 patchVnode
如果为相同节点,patchVnode用来比较两个虚拟节点的子节点并更新其子节点对应的真实Dom节点。
3.3 updateChildren
如果都有子节点,进行updateChildren,判断如何对新老节点进行操作。
总结
在diff中,只对同层的子节点进行比较,放弃跨级的节点比较,使得时间复杂从O(n3)降低值O(n),也就是说,只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。