之前在学习React源码过程中一直对reconcileChildren的reconcileChildrenArray方法很糊,好多次对照着源码看网络的视频、博文都没能形成一个很清晰的认识。这不,最近想再沉下心把这个方法再理解下,于是在网上再次找到一篇博文阅读了下,总算是有了一个比较清晰的认识了。在此记录下自己的理解,也希望自己的解读能够帮助到同样不太清楚这个方法的小伙伴吧。
此方法用来调和子元素为数组的情况:
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
...
if (isArray(newChild)) {
// newChild对应ClassComponent的this.render方法返回值,
// 或者FunctionComponent执行的返回值
return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime);
} ...
}
下图是reconcileChildrenArray方法的整体结构以及各块的说明:
下面举个例子进行说明:
情况1:
// 状态改变之前
<li key="0">0</li>
<li key="1">1</li>
<li key="2">2</li>
// 状态改变之后
<li key="0">0</li>
<li key="1">1</li>
<div key="2">2</div>
<li key="3">3</li>
newIndex为0、1时,前后的key、type相等节点可复用,newIndex为2时,type不等,跳出循环,此时oldFiber还有key === 2的节点未遍历,newChildren剩下key === 2、key === 3未遍历,此时去走图中编号4的逻辑进行节点的复用或者创建。
情况2:
// 状态改变之前
<li key="0">0</li>
<li key="1">1</li>
// 状态改变之后情况1>
<li key="0">00</li>
<li key="1">11</li>
// 状态改变之后情况2>
<li key="0">0</li>
<li key="1">1</li>
<li key="2">2</li>
// 状态改变之后情况3>
<li key="0">0</li>
这里情况1>、2>、3>分别代表第一次循环过后可能出现的三种情况,即:
1> newChildren与oldFiber都遍历完成,reconcileChildrenArray完成;
2> newChildren未完成,oldFiber遍历完成,此时去走图中编号3的逻辑进行节点的创建;
3> newChildren完成,oldFiber未遍历完成,此时去走图中编号2的逻辑进行剩下oldFiber节点的删除;
到此,整个ChildrenArray的diff逻辑大概就是这个样子。在这个函数中还有一项比较重要的顺序优化手段,这就是lastPlacedIndex的作用,我的理解是它主要用来提升状态改变后的dom操作效率,即尽可能的减少dom操作,典型的就是尽可能的减小dom的移动操作,如下图示例:
过程描述:
当然这种算法也会出现性能最差的一种情况,见下图:
此时完成后最终的commit dom操作是旧节点key0、key1向后移动,key2保持原位置不动。但从实际情况来说此处最高效的dom操作应该是旧节点key0、key1不动,key2移动到最前面。所以在实际写代码过程中应该尽量避免将最后一个节点更新到第一个节点的位置。
React源码中还有很多优秀的实践,比如高效的位运算、链表的应用使得更新可中断,高效的调度算法,他们的设计思路是非常值得学习和借鉴的,希望自己以后会有更多的总结输出。
本文的写作有参考如下文章,在此对你们的分享表示感谢!
1.https://blog.csdn.net/qiwoo_weekly/article/details/106345767;
2.https://blog.csdn.net/susuzhe123/article/details/107890118
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!