什么是虚拟Dom?
VDOM,也叫虚拟DOM,它是存在于内存中的Dom的形式,也就是一个js对象树,这个对象树中包含了真实Dom的层次关系和一些基本属性。
比如:
var element = {
element: 'ul',
props: {
id:"ulist"
},
children: [
{ element: 'li', props: { id:"first" }, children: ['这是第一个List元素'] },
{ element: 'li', props: { id:"second" }, children: ['这是第二个List元素'] }
]
}
为啥React使用虚拟Dom代替真Dom?(为啥虚拟Dom会提升性能)
在传统的 Web 应用中,js是直接操作Dom元素增删改,会导致性能降低。
而虚拟Dom的产生,相当于在js和真实Dom加一个缓冲,将js产生的所有操作统一收集起来,利用react diff剔除没有必要的操作,统一更新一次真实Dom。
过程如下图:
React diff算法
diff算法的作用及优势
计算出Virtual DOM中真正变化的部分,并只针对该部分进行原生DOM操作,而非重新渲染整个页面。
传统diff算法通过循环递归对节点进行依次对比,算法复杂度达到 O(n^3) ,n是树的节点数。 而React diff算法用三大策略将算法复杂度转化为O(n);
DIFF算法在执行时有三个维度,分别是Tree DIFF、Component DIFF和Element DIFF,执行时按顺序依次执行,它们的差异仅仅因为DIFF粒度不同、执行先后顺序不同。
而这三个维度分别为:
tree diff:
将树形结构进行分层比较,即将两棵树的同一层级的Dom节点进行比较(同一个父节点下的所有子节点)
当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。
这样只需要遍历一遍树形结构,就能完成两棵树的比较。
如下图:
左边是旧属,右边是新属,第一层是R组件,一模一样,不会发生变化;第二层进入Component DIFF,同一类型组件继续比较下去,发现A组件没有,所以直接删掉A、B、C组件;继续第三层,重新创建A、B、C组件。
component diff
react对不同组件的比较存在三种情况:
1.同一类型的两个组件,按tree diff比较同一层级的节点即可;
2.同一类型的两个组件,例如组件A要变为组件B,但是其Virtual DOM没有任何变化,(如果能确切知道这点将节约大量diff的时间),那么用户可以通过componentShouldUpdate这个钩子函数来判断是否应该diff;
3.如果不是同一类型的组件,则将这个即将被改变的组件判断为脏组件(dirty component),从而替换掉整个组件的节点;如下图: D组件和G组件不是同一类型的组件
从而删除 组件D及其子节点,创建组件G及其子节点。
element diff:
element diff紧接着以上同一类型组件继续比较下去。
节点处于同一层级时,React diff 提供了三种节点操作,分别为:INSERT_MARKUP(插入)、MOVE_EXISTING(移动)和 REMOVE_NODE(删除)。
当遇到列表中的相同元素时,他的比较策略是比较元素的key值,先遍历一遍列表,先确定插入和删除的元素,最后确定移动的元素。
如下图:
第一步将D删掉,第二步增加E,再次执行时A和B只需要移动位置即可。
总结:
React 通过制定大胆的 diff 策略,将 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题;
React 通过分层求异的策略,对 tree diff 进行算法优化;
React 通过相同类生成相似树形结构,不同类生成不同树形结构的策略,对 component diff 进行算法优化;
React 通过设置唯一 key的策略,对 element diff 进行算法优化;