11[ 点击] ( https://github.com/neroneroffy/react-source-code-debug ) 进入React源码调试仓库。
22
3- 插入DOM节点操作的是fiber节点上的stateNode,对于原生DOM类型的fiber节点来说stateNode存储着DOM节点。插入节点的操作就是循着fiber树把DOM节点插入到真实的DOM树中 。
3+ 插入DOM节点操作的是fiber节点上的stateNode,对于原生DOM类型的fiber节点来说stateNode存储着DOM节点。commit阶段插入节点的操作就是循着fiber树把DOM节点插入到真实的DOM树中 。
44
55` commitPlacement ` 是插入节点的入口,
66``` javascript
@@ -14,6 +14,7 @@ function commitMutationEffectsImpl(
1414
1515 switch (primaryEffectTag) {
1616 case Placement: {
17+ // 插入操作
1718 commitPlacement (fiber);
1819 fiber .effectTag &= ~ Placement;
1920 break ;
@@ -26,10 +27,10 @@ function commitMutationEffectsImpl(
2627
2728```
2829
29- 它的功能明确,将需要被执行stateNode插入操作的fiber节点称为目标节点。
30+ 我们将需要被执行插入操作的fiber节点称为目标节点, ` commitPlacement ` 函数的功能如下:
30311 . 找到目标节点DOM层面的父节点(parent)
31- 2 . 根据目标节点类型,改变parent
32- 3 . 如果目标节点对应的DOM节点目前只有文字内容,类似` <div>hello</div> ` ,并且持有ContentReset的effectTag ,那么插入节点之前先设置一下文字内容
32+ 2 . 根据目标节点类型,找到对应的parent
33+ 3 . 如果目标节点对应的DOM节点目前只有文字内容,类似` <div>hello</div> ` ,并且持有ContentReset(内容重置)的effectTag ,那么插入节点之前先设置一下文字内容
33344 . 找到基准节点
34355 . 执行插入
3536
@@ -85,12 +86,12 @@ function commitPlacement(finishedWork: Fiber): void {
8586
8687```
8788
88- 这里要明确的一点是DOM节点插入到哪,也就是第二步 ** 根据目标节点类型,改变parent ** 。
89+ 这里要明确的一点是DOM节点插入到哪,也就是要 ** 根据目标节点类型,找到对应的parent ** 。
8990
9091如果是` HostRoot ` 或者` HostPortal ` 类型的节点,第一它们都没有对应的DOM节点,第二实际渲染时它们会将DOM子节点渲染到对应的外部节点上(containerInfo)。
9192所以当fiber节点类型为这两个时,就将节点插入到这个外部节点上,即:
9293``` javascript
93- // 改变parent为fiber上的containerInfo
94+ // 将parent赋值为fiber上的containerInfo
9495parent = parentStateNode .containerInfo
9596
9697...
@@ -111,12 +112,11 @@ insertOrAppendPlacementNode(finishedWork, before, parent);
111112React插入节点的时候,分两种情况,新插入的DOM节点在它插入的位置是否已经有兄弟节点,没有,执行` parentInstance.appendChild(child) ` ,
112113有,调用` parentInstance.insertBefore(child, beforeChild) ` 。这个` beforeChild ` 就是上文提到的` before ` ,它是新插入的DOM节点的基准节点,
113114有了它才可以在父级DOM节点已经存在子节点的情况下,将新节点插入到正确的位置。试想如果已经有子节点还用` parentInstance.appendChild(child) ` 去插入,
114- 那是不是就把新节点插入到最末尾了?这显然是不对的。所以找到before的位置十分重要,它通过 ` getHostSibling ` 函数来定位到。
115+ 那是不是就把新节点插入到最末尾了?这显然是不对的。所以找到before的位置十分重要,` before ` (基准节点)通过 ` getHostSibling ` 函数来定位到。
115116
116117我们用一个例子来说明一下` getHostSibling ` 的原理:
117118
118- p为新生成的DOM节点。a为已存在且无变化的DOM节点。它们在fiber树中的位置如下,p需要插入到DOM树中,我们可以根据这棵fiber树
119- 来推断出最终的DOM树形态。
119+ p为新生成的DOM节点。a为已存在且无变化的DOM节点。它们在fiber树中的位置如下,p需要插入到DOM树中,我们可以根据这棵fiber树来推断出最终的DOM树形态。
120120```
121121 Fiber树 DOM树
122122
@@ -163,12 +163,11 @@ h1是原生DOM节点并且h1已存在于DOM树,那么h1作为结果返回,p
1631632 . 过滤不出来就查找同级节点的子节点,过滤出原生DOM组件。
1641643 . 重复查找兄弟节点再查找子节点的过程,直到再也找不到兄弟节点。
1651654 . 向上查找到父节点,兄对父节点也重复前三步。
166- 5 . 直到过滤出原生DOM节点,判断它如果不是需要插入的节点 ,那么它作为结果返回,新节点需要插入到它的前面。
166+ 5 . 直到过滤出原生DOM节点,如果该DOM节点不是需要插入的节点 ,那么它作为结果返回,也就是定位到了 ` before ` (基准节点) ,新节点需要插入到它的前面。
167167
168168这其中有如下规律:
169169
170- ** ` 需要插入的节点 ` 如果有同级fiber节点且是原生DOM节点,那么它一定是插入到这个节点之前的。如 果同级节点不是
171- 原生DOM节点,那么它和同级节点的子节点` 在DOM层面是兄弟节点 ` 的关系。**
170+ ** ` 需要插入的节点 ` 如果有同级fiber节点且是原生DOM节点,那么它一定是插入到这个节点之前的。如果同级节点不是原生DOM节点,那么它和同级节点的子节点` 在DOM层面是兄弟节点 ` 的关系。**
172171
173172** ` 需要插入的节点 ` 如果没有同级节点,那么它和父节点的兄弟节点的子节点` 在DOM层面是兄弟节点 ` 的关系。**
174173
@@ -265,11 +264,9 @@ insertOrAppendPlacementNode(finishedWork, before, parent);
265264* before: ` <Child/> ` 的sibling节点,该场景下为null
266265* parent: ` <Child/> ` 的parent节点,也就是` div#App `
267266
268- 进入函数,它的任务是将DOM节点插入到parent之下或before之前,如果finishedWork是原生DOM节点,那么依据有无before来决定节点的插入方式,
269- 无论哪种方式都会将DOM实实在在地插入到正确的位置上。
267+ 进入函数,它的任务是将DOM节点插入到parent之下或before之前,如果finishedWork是原生DOM节点,那么依据有无before来决定节点的插入方式,无论哪种方式都会将DOM实实在在地插入到正确的位置上。
270268
271- 如果不是原生DOM节点,就是` <Child/> ` 这种,不能对它进行插入操作,那么怎么办呢?向下,从它的child切入,再次调用` insertOrAppendPlacementNode ` ,
272- 也就是递归地调用自己,将child一个不剩地全插入到parent中。在例子中,会把p插入到parent。
269+ 如果不是原生DOM节点,就是` <Child/> ` 这种,不能对它进行插入操作,那么怎么办呢?向下,从它的child切入,再次调用` insertOrAppendPlacementNode ` ,也就是递归地调用自己,将child一个不剩地全插入到parent中。在例子中,会把p插入到parent。
273270
274271此时` <Child/> ` 的子节点已经全部完成插入,这时会再找到p的兄弟节点span,对它进行插入,然后发现span还有兄弟节点h1,将h1也插入。
275272
@@ -342,16 +339,15 @@ Placement --> <Child/>----> span / | \
342339```
343340
344341# 总结
345- 结合实际插入节点产生的问题不难总结出commit阶段节点插入过程的特点 :
342+ 结合实际插入节点产生的问题不难总结出commit阶段插入节点过程的特点 :
3463431 . 定位DOM节点插入的正确位置
3473442 . 避免DOM节点的多余插入
348345
349346找到基准节点before是第1点的关键,有了基准节点就能知道即将插入的父级节点上是否有已经存在,并且位置在目标节点之后的子节点。根据有无基准节点来决定执行哪种插入策略。
350347
351- 如何避免DOM节点的多余插入呢?上面分析插入过程的时候已经讲过,只会将目标节点的第一层子DOM节点插入到正确的位置,因为子DOM节点的插入工作已经完成了。这和effectList中
352- 收集的fiber节点的顺序有关,因为是自下而上收集的,所以fiber的顺序也是自下而上,导致DOM节点的插入也是自下而上的,可以类比一下累加的过程。
348+ 如何避免DOM节点的多余插入呢?上面分析插入过程的时候已经讲过,只会将目标节点的第一层子DOM节点插入到正确的位置,因为子DOM节点的插入工作已经完成了。这和effectList中收集的fiber节点的顺序有关,因为是自下而上收集的,所以fiber的顺序也是自下而上,导致DOM节点的插入也是自下而上的,可以类比一下累加的过程。
353349
354- 如下,可以看到最终的effestList中 ,最下层的节点排在最前面:
350+ 如下,可以看到最终的effectList中 ,最下层的节点排在最前面:
355351
356352![ ] ( http://neroht.com/commit_fiber_effect_list.jpg )
357353
0 commit comments