虚拟DOM
虚拟DOM是什么
虚拟DOM就是一个普通的JS对象,包含tag、props、children三个属性,用JS对象的形式来表示一棵真实的DOM树。
优点
减少DOM操作
- 减少操作次数:虚拟DOM可以将多次操作合并为一次操作。比如添加1000个节点,原生DOM操作1000次,而虚拟DOM只需要操作一次。
- 减小操作范围:虚拟DOM借助DOM diff,可以把多余的操作省去。比如添加1000个节点,而原本有990个,原生DOM需要添加1000个,虚拟DOM只需要添加10个。
跨平台
虚拟DOM不仅可以变成DOM,还可以变成小程序、iOS和Android应用等,因为虚拟DOM本质是一个对象。
缺点
- 需要用额外的创建函数,如createElement、h。
- 改进方法:转译。通过JS来简化成XML写法。
- 转译的缺点:严重依赖打包工具,需要另外的构建过程。
- 规模较大时,React会崩溃,原生DOM速度较快;规模较小时,虚拟DOM速度较快。
DOM diff
DOM diff是什么
DOM diff的主要体现就是一个比较函数,命名为 patch。
patch函数的主要写法:
patches = patch(oldVNode, newVNode)
//patches 就是要运行的 DOM 操作,可能长这样:
[
{type: 'INSERT', vNode: ... },
{type: 'TEXT', vNode: ... },
{type: 'PROPS', propsPatch: [...]}
]
优点
减小操作范围:diff 算法仅在两个树的同级的虚拟节点之间做比较,递归地进行比较,最终实现整个 DOM 树的更新。
实现
DOM diff 在做比较时分为三个层级:
- Tree Diff(层级比较):先进行树结构的层级比较,对同一个父节点下的所有子节点进行比较;接着看节点是什么类型的,是组件就做 Component Diff;如果节点是标签或者元素,就做 Element Diff。
- Component Diff (组件比较):若组件类型相同,则继续按照层级比较其虚拟 DOM的结构;如果组件类型不同,则替换整个组件的所有内容。
- Element Diff (元素比较):如果节点是原生标签,则看标签名做比较是否相同来决定替换还是更新属性,然后进入标签后代递归 Tree Diff。
缺点
当 DOM 对同级节点做比较的时候,diff 提供了三个节点操作,分别是:删除、插入和移动。
如以下列表:
const list = [
{
id: 1,
name: 'test1',
},
{
id: 2,
name: '我被删除了',
},
{
id: 3,
name: 'test3',
},
]
<div v-for="(item, index) in list" :key="index" >{{item.name}}</div>
如果使用index作为key,则diff在遍历DOM树时,不能区分每一个分支。此时,如果删去id为2的一项,则id为3的项的index值变成1,即它会成为第二项,造成的效果是删除了原本id为3的数据。此时list:
list = [
{
id: 1,
name: 'test1',
},
{
id: 2,
name: '我被删除了',
}
]
解决key问题
最好的办法是使用数组中不会变化的那一项作为key值,对应到项目中,即每条数据都有一个唯一的id,来标识这条数据的唯一性。