虚拟 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。