Skip to content

Commit 0908134

Browse files
committed
feat(reconciler): 完成 render 阶段源码注释以及调用栈图
1 parent de982c6 commit 0908134

21 files changed

+10492
-79
lines changed

Diff for: docs/img/commit.dio

+2,127
Large diffs are not rendered by default.

Diff for: docs/img/render.dio

+4,059
Large diffs are not rendered by default.

Diff for: docs/img/test.dio

+4,060
Large diffs are not rendered by default.

Diff for: docs/render阶段/beginWork阶段/处理更新.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ this.setState({val: 6});
9898
结论是:这是因为方便定位到链表的第一个元素。updateQueue指向它的最后一个update,updateQueue.next指向它的第一个update。
9999

100100
试想一下,若不使用环状链表,updateQueue指向最后一个元素,需要遍历才能获取链表首部。即使将updateQueue指向第一个元素,那么新增update时仍然要遍历到尾部才能将新增的接入链表。而环状链表,只需记住尾部,无需遍历操作就可以找到首部。理解概念是重中之重,下面再来看一下实现:
101-
```
101+
``` js
102102
function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
103103
const updateQueue = fiber.updateQueue;
104104
if (updateQueue === null) {

Diff for: src/App.css

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
body{
2+
background-color: #0D1117;
3+
color: #ddd;
4+
}
15
#root {
26
position: relative;
37
}
@@ -7,8 +11,6 @@
711
position: absolute;
812
top: 20px;
913
left: 30%;
10-
} {
11-
1214
}
1315
.App {
1416
text-align: center;

Diff for: src/App.js

+24-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { useEffect, useState, } from 'react';
2+
import ReactDOM from 'react-dom'
23
import State from './components/State'
34
import LanesDemo from './components/LanesDemo'
45
import AppSibling from './components/AppSibling'
@@ -22,41 +23,43 @@ function App() {
2223

2324
useEffect(() => {
2425
console.log('useEffect被执行');
26+
27+
console.log("%c'useEffect被执行 %c", "color:green",)
2528
return () => {
2629
console.log('useEffect 卸载函数被执行');
2730
}
2831
}, [])
2932

30-
const [count, setCount] = useState(1)
33+
const [flag, setFlag] = useState(true)
3134

3235
// 事件系统
3336
// return <EventDemo/>
3437

3538
// return <Hooks/>
3639
// fiber树
3740

38-
const a = <div>
39-
<h2 key="hello">hello</h2>
40-
<p key="world">world</p>
41-
</div>
42-
43-
// 顺序不一致
44-
// const b = <div>
45-
// <p key="world">world</p>
46-
// <h2 key="hello">hello</h2>
47-
// </div>
48-
49-
// 类型不一致
50-
const b = <div>
51-
<h6 key="hello">hello</h6>
52-
<p key="world">world</p>
53-
</div>
54-
5541
return (
56-
<div className="App" onClick={() => setCount(count + 1)}>
57-
{count % 2 ? a : b}
42+
<div className="App" onClick={() => {
43+
debugger;
44+
setFlag(!flag)
45+
}}>
46+
{
47+
flag
48+
? (
49+
<div>
50+
<h2 key="hello" className="h2-wrap">hello</h2>
51+
<p key="world" className="p-wrap">world</p>
52+
</div>
53+
)
54+
: (<div>
55+
<h6 key="hello">hello</h6>
56+
<p key="world">world</p>
57+
</div>)
58+
}
59+
5860
{/* <span>我是span {count}</span> */}
5961
{/* <h2>我是h2</h2> */}
62+
{/* {ReactDOM.createPortal} */}
6063
</div>
6164
);
6265
{/* <span className={'app-span'} onClick={() => setCount(count + 1)}>App{count}</span> */ }

Diff for: src/react/v17.0.2/react-dom/src/client/ReactDOMLegacy.js

+2
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ function legacyRenderSubtreeIntoContainer(
253253
// Update 批量更新
254254
updateContainer(children, fiberRoot, parentComponent, callback);
255255
}
256+
257+
// 获取根的真实dom
256258
return getPublicRootInstance(fiberRoot);
257259
}
258260

Diff for: src/react/v17.0.2/react-dom/src/client/ReactDOMRoot.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ ReactDOMRoot.prototype.unmount = ReactDOMLegacyRoot.prototype.unmount = function
111111
});
112112
};
113113

114-
// 初始化rendr 时 options 的值是 { hydrate: true, } 和 undefined,非服务端渲染时是 undefined
114+
// legacy 模式 render() 初始化rendr 时 options 的值是 { hydrate: true, } 和 undefined,非服务端渲染时是 undefined
115115
function createRootImpl(
116116
container: Container,
117117
tag: RootTag,
@@ -176,6 +176,7 @@ function createRootImpl(
176176
return root;
177177
}
178178

179+
// concurrent 模式
179180
export function createRoot(
180181
container: Container,
181182
options?: RootOptions,

Diff for: src/react/v17.0.2/react-dom/src/events/DOMPluginEventSystem.js

+1
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ export function dispatchEventForPluginEventSystem(
631631
}
632632
}
633633

634+
// 批量更新
634635
batchedEventUpdates(() =>
635636
dispatchEventsForPlugins(
636637
domEventName,

Diff for: src/react/v17.0.2/react-reconciler/src/ReactChildFiber.old.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ function ChildReconciler(shouldTrackSideEffects) {
735735
lanes: Lanes,
736736
): Fiber | null {
737737

738-
// 这个算法不能通过从两端搜索来优化,因为我们在纤维上没有反向指针。我想看看这个模型能走多远。如果它最终不值得进行权衡,我们可以稍后添加它。
738+
// 这个算法不能通过从两端搜索来优化,因为我们在 fiber 上没有反向指针。我想看看这个模型能走多远。如果它最终不值得进行权衡,我们可以稍后添加它。
739739
// 即使是两个端点的优化,我们也希望优化的情况下,有很少的改变和暴力的比较,而不是去 Map。它会首先在forward-only模式中探索命中那条路径,并且只有当我们注意到我们需要很多前瞻性时才会去寻找 Map。这不能像两个结束的搜索那样处理反转,但这是不寻常的。此外,为了使两端优化在Iterables上工作,我们需要复制整个集合。
740740
// 在第一次迭代中,我们只会在每次插入/移动时碰到坏情况(向Map添加所有内容)。
741741
// 如果你改变这个代码,也更新reconcileChildrenIterator(),它使用相同的算法。
@@ -1110,6 +1110,7 @@ function ChildReconciler(shouldTrackSideEffects) {
11101110
): Fiber {
11111111
const key = element.key;
11121112
let child = currentFirstChild;
1113+
// 第一次 render child 为 null
11131114
while (child !== null) {
11141115
// TODO: If key === null and child.key === null, then this only applies to
11151116
// the first item in the list.
@@ -1327,9 +1328,7 @@ function ChildReconciler(shouldTrackSideEffects) {
13271328
}
13281329
}
13291330
if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
1330-
// If the new child is undefined, and the return fiber is a composite
1331-
// component, throw an error. If Fiber return types are disabled,
1332-
// we already threw above.
1331+
// If the new child is undefined, and the return fiber is a composite component, throw an error. If Fiber return types are disabled, we already threw above.
13331332
switch (returnFiber.tag) {
13341333
case ClassComponent: {
13351334
if (__DEV__) {

Diff for: src/react/v17.0.2/react-reconciler/src/ReactFiber.old.js

+1
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ export function createFiberFromTypeAndProps(
505505
lanes: Lanes,
506506
): Fiber {
507507
let fiberTag = IndeterminateComponent;
508+
// 如果我们知道最终类型是什么,则设置 resolved tyle, IE。这不是 lazy。
508509
// The resolved type is set if we know what the final type will be. I.e. it's not lazy.
509510
let resolvedType = type;
510511
if (typeof type === 'function') {

Diff for: src/react/v17.0.2/react-reconciler/src/ReactFiberBeginWork.old.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1137,15 +1137,16 @@ function pushHostRootContext(workInProgress) {
11371137
root.pendingContext !== root.context,
11381138
);
11391139
} else if (root.context) {
1140-
// Should always be set
1140+
// Should always be set 应始终设置
1141+
// 推送顶级上下文对象
11411142
pushTopLevelContextObject(workInProgress, root.context, false);
11421143
}
11431144
pushHostContainer(workInProgress, root.containerInfo);
11441145
}
11451146

11461147
// 更新根组件
11471148
function updateHostRoot(current, workInProgress, renderLanes) {
1148-
// 暂时没看
1149+
// 在堆栈中记录了一些数据
11491150
pushHostRootContext(workInProgress);
11501151
const updateQueue = workInProgress.updateQueue;
11511152
invariant(
@@ -1165,6 +1166,10 @@ function updateHostRoot(current, workInProgress, renderLanes) {
11651166
// 计算到新的 state 给 memoizedState 添加上值 cache,element
11661167
processUpdateQueue(workInProgress, nextProps, null, renderLanes);
11671168
// 取到新的 state
1169+
/**
1170+
cache: Map(0) {}
1171+
element: {$$typeof: Symbol(react.element)
1172+
*/
11681173
const nextState = workInProgress.memoizedState;
11691174

11701175
const root: FiberRoot = workInProgress.stateNode;
@@ -3503,6 +3508,9 @@ function beginWork(
35033508
didReceiveUpdate = false;
35043509
}
35053510

3511+
// 在进入开始阶段之前,清除挂起的更新优先级
3512+
// 这假设我们即将评估组件并处理更新队列。
3513+
// 但是,有一个例外:SimpleMemoComponent 有时会在开始阶段的后期退出。 这表明我们应该将此分配移出公共路径并进入每个分支。
35063514
// Before entering the begin phase, clear pending update priority.
35073515
// TODO: This assumes that we're about to evaluate the component and process
35083516
// the update queue. However, there's an exception: SimpleMemoComponent

Diff for: src/react/v17.0.2/react-reconciler/src/ReactFiberCommitWork.old.js

+12-14
Original file line numberDiff line numberDiff line change
@@ -1547,22 +1547,23 @@ function commitPlacement(finishedWork: Fiber): void {
15471547
}
15481548

15491549
// Recursively insert all host nodes into the parent.
1550+
// 递归向上找到 有真实 dom 的fiber
15501551
const parentFiber = getHostParentFiber(finishedWork);
15511552

15521553
// Note: these two variables *must* always be updated together.
15531554
let parent;
15541555
let isContainer;
15551556
const parentStateNode = parentFiber.stateNode;
15561557
switch (parentFiber.tag) {
1557-
case HostComponent:
1558+
case HostComponent: // 原生dom 标签
15581559
parent = parentStateNode;
15591560
isContainer = false;
15601561
break;
1561-
case HostRoot:
1562+
case HostRoot: // 根元素
15621563
parent = parentStateNode.containerInfo;
15631564
isContainer = true;
15641565
break;
1565-
case HostPortal:
1566+
case HostPortal: // 通过 createPortal 创建的元素
15661567
parent = parentStateNode.containerInfo;
15671568
isContainer = true;
15681569
break;
@@ -1576,15 +1577,19 @@ function commitPlacement(finishedWork: Fiber): void {
15761577
}
15771578
if (parentFiber.flags & ContentReset) {
15781579
// Reset the text content of the parent before doing any insertions
1580+
// 在执行任何插入之前,重置父元素的文本内容
15791581
resetTextContent(parent);
1582+
// 清除 ContentReset 的 effect 标记
15801583
// Clear ContentReset from the effect tag
15811584
parentFiber.flags &= ~ContentReset;
15821585
}
15831586

1587+
// 获取兄弟节点
15841588
const before = getHostSibling(finishedWork);
15851589
// We only have the top Fiber that was inserted but we need to recurse down its
15861590
// children to find all the terminal nodes.
15871591
if (isContainer) {
1592+
// 将放置节点插入或附加到容器中
15881593
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
15891594
} else {
15901595
insertOrAppendPlacementNode(finishedWork, before, parent);
@@ -2243,6 +2248,9 @@ function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
22432248
}
22442249
}
22452250

2251+
// 下面的switch语句只关心放置、更新和删除。
2252+
// 为了避免需要为每个可能的位图值添加一个案例,我们从效果标签中删除了辅助效果并打开该值。
2253+
22462254
// The following switch statement is only concerned about placement,
22472255
// updates, and deletions. To avoid needing to add a case for every possible
22482256
// bitmap value, we remove the secondary effects from the effect tag and
@@ -2600,17 +2608,7 @@ function commitPassiveUnmountEffects_begin() {
26002608
}
26012609

26022610
if (deletedTreeCleanUpLevel >= 1) {
2603-
// A fiber was deleted from this parent fiber, but it's still part of
2604-
// the previous (alternate) parent fiber's list of children. Because
2605-
// children are a linked list, an earlier sibling that's still alive
2606-
// will be connected to the deleted fiber via its `alternate`:
2607-
//
2608-
// live fiber
2609-
// --alternate--> previous live fiber
2610-
// --sibling--> deleted fiber
2611-
//
2612-
// We can't disconnect `alternate` on nodes that haven't been deleted
2613-
// yet, but we can disconnect the `sibling` and `child` pointers.
2611+
// A fiber was deleted from this parent fiber, but it's still part of the previous (alternate) parent fiber's list of children. Because children are a linked list, an earlier sibling that's still alive will be connected to the deleted fiber via its `alternate`: live fiber --alternate--> previous live fiber --sibling--> deleted fiber We can't disconnect `alternate` on nodes that haven't been deleted yet, but we can disconnect the `sibling` and `child` pointers.
26142612
const previousFiber = fiber.alternate;
26152613
if (previousFiber !== null) {
26162614
let detachedChild = previousFiber.child;

Diff for: src/react/v17.0.2/react-reconciler/src/ReactFiberCompleteWork.old.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ function completeWork(
935935
currentHostContext,
936936
workInProgress,
937937
);
938-
938+
// debugger
939939
// 将当前元素的所有 真实dom child 追加到 当前元素下
940940
appendAllChildren(instance, workInProgress, false, false);
941941

Diff for: src/react/v17.0.2/react-reconciler/src/ReactFiberHooks.old.js

+4
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,10 @@ function dispatchAction<S, A>(
19921992
try {
19931993
const currentState: S = (queue.lastRenderedState: any);
19941994
const eagerState = lastRenderedReducer(currentState, action);
1995+
1996+
// 将急切计算的状态和用于计算它的reducer保存在更新对象上。
1997+
// 如果 reducer 在我们进入渲染阶段时还没有改变,那么无需再次调用 reducer ,就可以使用 eager 状态。
1998+
19951999
// Stash the eagerly computed state, and the reducer used to compute
19962000
// it, on the update object. If the reducer hasn't changed by the
19972001
// time we enter the render phase, then the eager state can be used

Diff for: src/react/v17.0.2/react-reconciler/src/ReactFiberInterleavedUpdates.old.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
import type {UpdateQueue as HookQueue} from './ReactFiberHooks.old';
1111
import type {SharedQueue as ClassQueue} from './ReactUpdateQueue.old';
1212

13+
// 在当前渲染期间接收更新的所有更新队列的数组。 当这个渲染退出时,无论是因为它完成还是因为它被中断,交错更新将被转移到队列的主要部分。
1314
// An array of all update queues that received updates during the current
1415
// render. When this render exits, either because it finishes or because it is
1516
// interrupted, the interleaved updates will be transfered onto the main part
1617
// of the queue.
1718
let interleavedQueues: Array<
1819
HookQueue<any, any> | ClassQueue<any>,
1920
> | null = null;
20-
21+
2122
export function pushInterleavedQueue(
2223
queue: HookQueue<any, any> | ClassQueue<any>,
2324
) {
@@ -30,8 +31,10 @@ export function pushInterleavedQueue(
3031

3132
export function enqueueInterleavedUpdates() {
3233

33-
/* 将交错更新传输到主队列上。每个队列都有一个“挂起”字段和一个“交错”字段。
34-
当它们不为空时,它们指向循环链表中的最后一个节点。我们需要将交叉列表添加到待处理列表的末尾,方法是将它们连接到一个单一的循环列表中。 */
34+
/* 将并发更新传输到主队列。每个队列都有一个“pending”字段和一个“interleaved”字段。
35+
当它们不为空时,它们指向循环链表中的最后一个节点。我们需要将交错列表附加到待处理列表的末尾,方法是将它们连接到一个单一的循环列表中。 */
36+
// Transfer the interleaved updates onto the main queue. Each queue has a `pending` field and an `interleaved` field. When they are not null, they point to the last node in a circular linked list. We need to append the interleaved list to the end of the pending list by joining them into a single, circular list.
37+
3538
// Transfer the interleaved updates onto the main queue. Each queue has a
3639
// `pending` field and an `interleaved` field. When they are not null, they
3740
// point to the last node in a circular linked list. We need to append the

Diff for: src/react/v17.0.2/react-reconciler/src/ReactFiberLane.old.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -668,10 +668,10 @@ export function markRootUpdated(
668668

669669
/* 如果有任何暂停的过渡,则此新更新可能会取消阻止它们。 清除暂停的车道,以便我们可以尝试再次渲染它们。
670670
671-
TODO:实际上,我们只需要取消挂起更新光纤的“ subtreeLanes”中的通道或返回路径的更新通道即可。
672-
这将排除不相关的同级树中的暂挂更新,此更新无法解除阻止。
671+
我们实际上只需要取消挂起更新 fiber 的“subtreeLanes”中的通道,或返回路径的更新 lanes 。这将排除不相关的兄弟树中挂起的更新,因为此更新无法取消阻止它。
673672
674-
如果传入的更新是空闲的,则不执行此操作,因为直到所有常规更新都完成后,我们才处理空闲的更新。 它不可能解开过渡。 */
673+
如果传入的更新是空闲的,我们不会这样做,因为在所有常规更新完成之前,我们永远不会处理空闲更新;不会解除对转换的阻止。
674+
*/
675675

676676
// 如果不相对 则把以下设置为 0
677677
if (updateLane !== IdleLane) {
@@ -685,7 +685,7 @@ export function markRootUpdated(
685685
* 那么eventTimes是这种形式: [ -1, -1, -1, 44573.3452, -1, -1 ]
686686
* 用一个数组去存储eventTimes,-1表示空位,非-1的位置与lanes中的1的位置相同
687687
* */
688-
const eventTimes = root.eventTimes;
688+
const eventTimes = root.eventTimes; // 长度为 31 的数组 每个值默认是 0
689689
// 根据 更新等级返回 索引
690690
const index = laneToIndex(updateLane);
691691
// We can always overwrite an existing timestamp because we prefer the most

Diff for: src/react/v17.0.2/react-reconciler/src/ReactFiberReconciler.old.js

+1
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ export function getPublicRootInstance(
389389
case HostComponent:
390390
return getPublicInstance(containerFiber.child.stateNode);
391391
default:
392+
// 返回真实 dom
392393
return containerFiber.child.stateNode;
393394
}
394395
}

0 commit comments

Comments
 (0)