深入React-虚拟Dom及React Diff

493 阅读3分钟

什么是虚拟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 进行算法优化;

参考:www.jianshu.com/p/3ba082201…