虚拟 DOM 和 DOM diff

156 阅读2分钟

虚拟 DOM

虚拟 DOM 是什么

虚拟 DOM就是用 JavaScript 对象表示 DOM 信息和结构,当状态变更的时候,重新渲染这个 JavaScript 的对象结构。用新的对象树去和旧的树进行对比,记录这两棵树差异。记录下来的不同就是需要对页面真正的 DOM 操作,然后把它们应用在真正的 DOM 树上,页面就变更了。

var element = {
  tagName: 'ul',
  props: {
    id: 'list'
  },
  children: [
    {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
    {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
    {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
  ]
}

虚拟 DOM 的优点

  • 大部分场景下性能比直接操作 DOM 高,能够合并 DOM 操作,还可以借助 DOM diff 省掉多余的操作 ,减少操作次数。
  • 无需手动操作 DOM ,提高开发效率
  • 方便跨平台

虚拟 DOM 的缺点

  • 需要额外的创建函数,如 createElement 或 h ,但可以被JSX等方式简化。

DOM diff

DOM diff 是什么

是一种比较两棵 DOM 树之间差异的算法。简单来说就是对新旧两棵树进行深度优先遍历,在相同层寻找差异,发现差异就记录下来,记录下来的结果再交给虚拟 DOM 进行真正 DOM 元素的操作。

DOM diff 的优点

可以做到只把变化的部分重新渲染,避免多余的的渲染过程,提高性能。

DOM diff 的问题

在同级节点对比时,删除中间的元素会被视作先修改成后面的再删除掉后面的:

[
    {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
    {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
    {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
]

// 删除 Item2 的过程

// 先将 Item2 修改成 Item3
[
    {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
    {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
    {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
]

// 再删除 Item3
[
    {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
    {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
]

这会导致 Item3 中存储的状态被误删,而保留下了本该被删除的 Item2 的状态。

在 Vue 中可以通过添加 :key 来辅助判断,使其直接留用 Item3 并删除 Item2。