React基于key的重排算法

我们知道,React在更新视图时,会比较新旧树的节点,计算出差异,然后根据差异来进行修改。对于同一层级的子节点,如果按顺序依次比较,发现不同则删去原有的,插入新的。这样虽然可行,但如果这一层只是更换了顺序,那么频繁地删除、插入节点,效率会很低。为了提高子元素对比的性能,React使用了基于key的算法,能够更好地复用已有的节点。

这里只介绍其实现思路,不展示具体的源代码。

简化模型

假如有5个人已经排好了队,我们按顺序标为1,2,3,4,5号。现在要重新排为3,2,1,5,4,那么怎样有一个标准的方法重排呢?

React的算法分为两步,先标记出需要移动的元素,再按一定的标准移动。

标记

第一步:根据新队列的顺序对元素遍历,如果新队列中未来在它左边的元素都已经在它左边,那么它可以不移动。

  1. 我们从3开始,3未来左边没有元素,所以3不用移动,而对于新队列3之后的元素,想要不移动,它原来的位置肯定要大于3,3成为第一个定位的标准;

  2. 2 < 3,标记为要移动;

  3. 1 < 3,标记为要移动;

  4. 5 > 3,不用移动,并成为新的定位标准;

  5. 4 < 5,标记为要移动。

标记结果如图:

移动

我们确定了固定和移动的元素,那么具体要如何以固定元素为参照来移动呢?移动DOM元素的方法有insertBefore、appendChild,这里就要用到这两种方法。

第二步:同样按新队列的顺序遍历,寻找之后第一个固定的元素。如果有,就insertBefore;没有就appendChild到最后。

移动过程如图:

实际实现

React不会一开始上来就尝试重排,而是按顺序依次比较,key值匹配时(包括都为null的情况),会一直持续下去。停止的原因三种:

  1. 原来的节点不够了,这时新的多余的节点都直接插入;

  2. 新的节点不够了,这时原有多余的节点都删除;

  3. 出现key值不匹配时,开始重排。

三种情况如下:

React会将旧节点根据key或者index存入Map结构中,然后遍历新节点,根据新节点的key或index在Map中寻找旧节点。如果没找到,证明这是一个全新节点,需要插入;如果找到了,则按之前的重排算法确定是否移动,并从Map中删去旧节点。当遍历完成后,Map中剩余的则是需要删除的节点。

参考

  1. React源码

推荐文章