惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
I
InfoQ
H
Help Net Security
GbyAI
GbyAI
博客园 - 叶小钗
Recent Announcements
Recent Announcements
Engineering at Meta
Engineering at Meta
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
爱范儿
爱范儿
Y
Y Combinator Blog
L
LangChain Blog
腾讯CDC
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
Stack Overflow Blog
Stack Overflow Blog
F
Fortinet All Blogs
G
Google Developers Blog
Apple Machine Learning Research
Apple Machine Learning Research
The GitHub Blog
The GitHub Blog
T
The Blog of Author Tim Ferriss
博客园 - Franky
D
Docker
Jina AI
Jina AI
罗磊的独立博客

蚊子的前端博客

微说 | 有的人觉得只要不考XX,一定能考好!-蚊子的前端博客 微说 | 春天来了-蚊子的前端博客 明天和意外不知道哪个先来-蚊子的前端博客 微说 | 既然错过了路口,就及时止损,重新规划路线上路-蚊子的前端博客 微说 | 2025年的出生人口数是792万-蚊子的前端博客 微说 | 工作年终总结,让写对接的接口的数量?-蚊子的前端博客 微说 | 温家宝:没有政治体制改革的成功 经济体制改革不可能进行到底-蚊子的前端博客 微说 | 违法犯罪了该不该被禁言?-蚊子的前端博客 微说 | 不明白为什么在推荐去俄罗斯旅游-蚊子的前端博客 微说 | 易中天论骗子-蚊子的前端博客 微说 | 一场大风吹散了秋天-蚊子的前端博客 微说 | 既要、又要、还要、更要!-蚊子的前端博客 又是一年的国庆雨季-蚊子的前端博客 微说 | 预估下2025年的出生人口数据-蚊子的前端博客 微说 | 可惜了我那些小时候的书本-蚊子的前端博客 微说 | 牛马有的是,驴不够了!-蚊子的前端博客 微说 | 能否有一条非户口也能高考的路-蚊子的前端博客 微说 | 小聊中医-蚊子的前端博客 微说 | 将要制作的一款新产品-蚊子的前端博客 微说 | 做人要有信-蚊子的前端博客 微说 | 好一个正义联盟-蚊子的前端博客 微说 | 都是见过吃过的主儿-蚊子的前端博客 微说 | 追求8小时工作制有错吗?-蚊子的前端博客 微说 | 如果尖锐的批评完全消失-蚊子的前端博客 微说 | 很好!-蚊子的前端博客 微说 | 封禁用户可以,但要告知具体原因-蚊子的前端博客 微说 | 面朝大海,春暖花开-蚊子的前端博客 微说 | 程序员的悲哀是什么?-蚊子的前端博客 微说 | 2025年出生人口的预测-蚊子的前端博客 前端在 LiveKit 中如何获取所有的参与者-蚊子的前端博客
React18 源码解析之 placeChild 的执行-蚊子的前端博客
author · 2022-09-19 · via 蚊子的前端博客

我们解析的源码是 React18.1.0 版本,请注意版本号。React 源码学习的 GitHub 仓库地址:https://github.com/wenzi0github/react

在 React fiber 对比的过程中,有用到 placeChild() 函数,这个函数是做什么的呢?

此方法是一种顺序优化手段,lastPlacedIndex 一直在更新,初始为 0,表示访问过的节点在旧集合中最右的位置(即最大的位置)。如果新集合中当前访问的节点比 lastPlacedIndex 大,说明当前访问节点在旧集合中就比上一个节点位置靠后,则该节点不会影响其他节点的位置,因此不用添加到差异队列中,即不执行移动操作。只有当访问的节点比 lastPlacedIndex 小时,才需要进行移动操作。

lastPlaceIndex 表示当前复用到的旧 fiber 的最大索引,初始时为 0。比如第一个新 fiber 节点复用的是最后一个旧 fiber 节点,那 lastPlaceIndex 就是最后那个旧 fiber 节点的索引值。

1. 样例 1 #

return !flag
  ? [<li key="0">0</li>, <li key="1">1</li>, <li key="2">2</li>]
  : [<li key="1">0</li>, <li key="2">2</li>, <li key="0">2</li>];

过程描述:

  1. lastPlaceIndex 初始时为 0;
  2. 新节点 key1,Map 集合中存在 key1 则取出复用,key1 老节点的 oldIndex 为 1,不满足 oldIndex < lastPlacedIndex,返回 oldIndex,并且赋值给 lastPlacedIndex 值更新为 1。
  3. 新节点 key2,Map 集合中存在 key2 则取出复用,key2 老节点的 oldIndex 为 2,不满足 oldIndex < lastPlacedIndex,返回 oldIndex,并且赋值给 lastPlacedIndex 值更新为 2。
  4. 新节点 key0,Map 集合中存在 key0 则取出复用,key0 老节点的 oldIndex 为 0,满足 oldIndex < lastPlacedIndex,则将 key0 标记为插入,返回 lastPlacedIndex。

2. 样例 2 #

return !flag
  ? [
      <li key="0">0</li>,
      <li key="1">1</li>,
      <li key="2">2</li>,
      <li key="3">2</li>
    ]
  : [
      <li key="1">1</li>,
      <li key="0">0</li>,
      <li key="3">3</li>,
      <li key="2">2</li>
    ];

过程描述:

  1. lastPlaceIndex 初始时为 0;
  2. 新节点 key1,Map 集合中存在 key1 则取出复用,key1 老节点的 oldIndex 为 1,不满足 oldIndex < lastPlacedIndex,返回 oldIndex,并且赋值给 lastPlacedIndex 值更新为 1。
  3. 新节点 key0,Map 集合中存在 key0 则取出复用,key0 老节点的 oldIndex 为 0,满足 oldIndex < lastPlacedIndex,则将 key0 标记为插入,返回 lastPlacedIndex。
  4. 新节点 key3,Map 集合中存在 key3 则取出复用,key3 老节点的 oldIndex 为 3,不满足 oldIndex < lastPlacedIndex,返回 oldIndex,并且赋值给 lastPlacedIndex 值更新为 3。
  5. 新节点 key2,Map 集合中存在 key2 则取出复用,key2 老节点的 oldIndex 为 2,满足 oldIndex < lastPlacedIndex,则将 key2 标记为插入,返回 lastPlacedIndex。

3. 样例 3: #

return !flag
  ? [
      <li key="0">0</li>,
      <li key="1">1</li>,
      <li key="2">2</li>,
      <li key="3">2</li>
    ]
  : [
      <li key="1">1</li>,
      <li key="5">5</li>,
      <li key="3">3</li>,
      <li key="0">0</li>
    ];

过程描述:

  1. lastPlaceIndex 初始时为 0;
  2. 新节点 key1,Map 集合中存在 key1 则取出复用,key1 老节点的 oldIndex 为 1,不满足 oldIndex < lastPlacedIndex,返回 oldIndex,并且赋值给 lastPlacedIndex 值更新为 1。
  3. 新节点 key5,Map 集合中不存在 key5 新建节点,不满足 current !== null,则将 key5 标记为插入,返回 lastPlacedIndex。
  4. 新节点 key3,Map 集合中存在 key3 则取出复用,key3 老节点的 oldIndex 为 3,不满足 oldIndex < lastPlacedIndex,返回 oldIndex,并且赋值给 lastPlacedIndex 值更新为 3。
  5. 新节点 key0,Map 集合中存在 key0 则取出复用,key0 老节点的 oldIndex 为 0,满足 oldIndex < lastPlacedIndex,则将 key0 标记为插入,返回 lastPlacedIndex。
  6. 剩余节点 key2 通过 existingChildren 遍历删除,被复用过的节点因为从 map 集合中已经移除了,所以这里的删除只是为被复用的。

4. 样例 4(性能差的一种情况) #

return !flag
  ? [<li key="0">0</li>, <li key="1">1</li>, <li key="2">2</li>]
  : [<li key="2">2</li>, <li key="0">0</li>, <li key="1">1</li>];

过程同上,但是这种操作会使得顺序优化算法失去效果,除了最后一个节点没有 effect,其他节点都会被执行插入操作,所以尽量避免将最后一个节点更新到第一个节点的位置操作。

参考文章: