react中diff算法和列表key值的作用及与vue中diff算法的区别

1,018 阅读5分钟

1.react中diff算法及列表key属性的作用

React在props或state发生改变时,会调用React的render方法,会创建一颗不同的树。

React需要基于这两颗不同的树之间的差别来判断如何有效的更新UI:

如果一棵树参考另外一棵树进行完全比较更新,那么即使是最先进的算法,该算法的复杂程度为 O(n 3 ),其中 n 是树中元素

的数量;

grfia.dlsi.ua.es/ml/algorith…

如果在 React 中使用了该算法,那么展示 1000 个元素所需要执行的计算量将在十亿的量级范围;

这个开销太过昂贵了,React的更新性能会变得非常低效;

于是,React对这个diff算法进行了优化,将其优化成了O(n),如何优化的呢?

同层节点之间相互比较,不会垮节点比较;

不同类型的节点,产生不同的树结构;

开发中,可以通过key来指定哪些节点在不同的渲染下保持稳定;


下面通过三步讲下react的diff算法更新虚拟dom和真实dom机制

情况一:对比不同类型的元素

  • 当节点为不同的元素,React会拆卸原有的树,并且建立起新的树:

    当一个元素从 a标签 变成 img标签,从 Article 变成 Comment,或从 button标签 变成 div都会触发一个完整的重建

    流程;

    当卸载一棵树时,对应的DOM节点也会被销毁,组件实例将执行 componentWillUnmount() 方法;

    当建立一棵新的树时,对应的 DOM 节点会被创建以及插入到 DOM 中,组件实例将执行 componentWillMount() 方法,

    紧接着 componentDidMount() 方法;

    比如下面的代码更改:

    React 会销毁Top 组件并且重新装载一个新的组件,而不会对Top 进行复用;

    节点类型不同


情况二:对比同一类型的元素

  • 当比对两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性。

    比如下面的代码更改:

    通过比对这两个元素,React 知道只需要修改 DOM 元素上的 className 属性;

    比如下面的代码更改:

    当更新 style 属性时,React 仅更新有所更变的属性。

    通过比对这两个元素,React 知道只需要修改 DOM 元素上的 color 样式,无需修改 fontWeight。

    如果是同类型的组件元素:

    组件会保持不变,React会更新该组件的props,并且调用componentWillReceiveProps() 和 componentWillUpdate() 方

    法;

    下一步,调用 render() 方法,diff 算法将在之前的结果以及新的结果中进行递归;


情况三:对子节点进行递归

在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。

我们来看一下在最后插入一条数据的情况:

前面两个比较是完全相同的,所以不会产生mutation;

最后一个比较,产生一个mutation,将其插入到新的DOM树中即可;

但是如果我们是在中间插入一条数据:

img

这样react中新树的第一项与旧树的第一项进行比较,不对应,产生一个mutation,以此类推,

React会对每一个子元素产生一个mutation,则是三个mutation,而不是保持first和second的不变;

这种低效的比较方式会带来一定的性能问题;

所以为了解决这种低效的性能问题,可以加入keys的优化

我们在react中遍历列表时,在字元素不加key属性的话总是会提示一个警告,让我们加入一个key属性:

  • 方式一:在最后位置插入数据

    • 这种情况,有无key意义并不大
  • 方式二:在前面插入数据

    • 这种做法,在没有key的情况下,所有的li都需要进行修改;

当子元素(这里的li)拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素:

在下面这种场景下,key为111和222的元素仅仅进行位移,不需要进行任何的修改;

将key为333的元素插入到最前面的位置即可;

  • key的注意事项

    key应该是唯一的;

    key不要使用随机数(随机数在下一次render时,会重新生成一个数字);

    使用index作为key,对性能是没有优化的;

2. vue中diff算法与react中diff算法区别

  1. vue中diff算法在对新老虚拟daom进行对比时,是从节点的两侧向中间对比,而react是从节点左侧开始对比,就好比将新老虚拟dom放入两个栈中,一对多依次对比;
  2. vue中在进行节点比较时节点的key值与元素类型相同,属性值不同,就会认为是不同节点,就会删除重建,而react中节点的key值与元素类型相同,属性值不同,react会认为是同类型节点,只是修改节点属性
  • 两者的共同点:

    都只进行同级比较,忽略了跨级操作;常见的现象就是对数组或者对象中的深层元素进行修改时,视图层监测不到其变化,故不会对dom进行更新,需调用一些特质方法来告诉视图层需要更新dom