面试|虚拟DOM,diff算法

68 阅读2分钟

一、虚拟DOM

虚拟Dom,就是一个用来描述真实Dom的对象,它有六个属性:

  1. sel表示当前节点标签名
  2. data内是节点的属性
  3. children表示当前节点的其他子标签节点
  4. elm表示当前虚拟节点对应的真实节点(这里暂时没有)
  5. key即为当前节点的key
  6. text表示当前节点下的文本
    其结构如下:
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算法进行同层级比较。