React源码-ReactFiberWorkLoop.js
# 0. 前言
# 1.completeUnitOfWork 逻辑分析
归阶段的核心函数:complteUnitOfWork
、completeWork
在分析源码之前,需要知道考虑到以下两类 DOM
结构,对于 completeWork
遍历逻辑是不同的。
结构1:
function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> </header> </div> ); }
1
2
3
4
5
6
7
8
9结构2:
function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
深度优先遍历的逻辑:
无论是上述两类结构,第一个进入
complteWork
的节点始终都是<img />
节点。对于第一类结构,就是典型的
do...while
语法,且不会跳出循环。<img />
<header />
<div />
伪代码结构如下:
var completedWork = unitOfWork; // <img /> 节点 do{ var returnFiber = completedWork.return; // 生成 DOM 节点并插入 stateNode completeWork(current, completedWork, subtreeRenderLanes); // 返回 父 Fiber 节点 completedWork = returnFiber; }while(completedWork)
1
2
3
4
5
6
7
8而对于第二类结构,会检查是否存在兄弟节点,如果存在会跳出循环逻辑。
// 是否存在兄弟节点需要深度优先遍历? const siblingFiber = completedWork.sibling; // 如果存在,则跳出循环 if (siblingFiber !== null) { workInProgress = siblingFiber; return; };
1
2
3
4
5
6
7截止条件:当父节点仍为
null
时,说明已经回到顶部节点(此处为App
节点),则跳出循环。
完整源码分析:
function completeUnitOfWork(unitOfWork) {
// 当前待处理的 workInProgress 节点【注:此时 stateNode = null】
var completedWork = unitOfWork;
do{
// 获取 current Fiber
var current = completedWork.alternate;
// 提前缓存父节点指针
var returnFiber = completedWork.return;
// completedWork.flags 记录当前是否完成遍历
if((completedWork.flags & Incomplete) === NoFlags){
// @todo
setCurrentFiber(completedWork);
var next = void 0; // undefined
// completedWork.mode // 若为同步模式为 `BlockingMode` ,异步为 `ConcurrentyMode`
if ((completedWork.mode & ProfileMode) === NoMode) {
// 进入性能模式
next = completeWork(current, completedWork, subtreeRenderLanes);
} else {
// 一般模式
next = completeWork(current, completedWork, subtreeRenderLanes); // Update render duration assuming we didn't error.
}
// complteWork 的返回值, 当不为 null 时跳出循环
if(next !== null){
workInProgress = next;
return;
}
if(returnFiber !== null && (returnFiber.flags & Incomplete) === NoFlags){
// 此阶段用于生成 EffectList 链表
}
}
// 是否存在兄弟节点需要深度优先遍历?
const siblingFiber = completedWork.sibling;
// 如果存在,则跳出循环
if (siblingFiber !== null) {
workInProgress = siblingFiber;
return;
};
// 返回 父 Fiber 节点
completedWork = returnFiber;
// 修改全局 workInProgress 对象
workInProgress = completedWork;
}while (completedWork !== null);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# completeWork
主逻辑
位置:【react-reconciler
】【ReactFiberComplteWork.old.js
】
complteWork
核心代码逻辑就是 switch...case
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
switch (workInProgress.tag){
/** 对于以下节点是不予以处理的 */
case IndeterminateComponent:
case LazyComponent:
case SimpleMemoComponent:
case FunctionComponent:
case ForwardRef:
case Fragment:
case Mode:
case Profiler:
case ContextConsumer:
case MemoComponent:
return null;
}
case xxx;
return null;
case HostComponent;
/** 见后续分析 */
return null
....
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
complteWork
的核心功能就是往 fiber
上挂载 stateNode
,但对于 fiber
结构并非与 DOM
结构一一对应的,如 FunctionComponent
和 Fragment
是典型不产生DOM
,只是作为一种 namespace
存在。
举例:
function A(){
return <B />
}
function B(){
return <C />
}
function C(){
return <h1>Hello world</h1>
}
2
3
4
5
6
7
8
9
10
11
对其中一个 case
进行分析:
case HostComponent: {
// 很重要:获取 `root` 对应的 DOM 节点, 如 React.render(<App>, rootDOM)
const rootContainerInstance = getRootHostContainer();
// img 对应的 tag 标签为 5
const type = workInProgress.type;
// 更新逻辑:current 不为空,且已生成对应的 DOM 节点【对于 HostComponent 是存在 DOM 结构的】
if(current !== null && workInProgress.stateNode != null){
// 更新 HostComponent 逻辑
updateHostComponent(
current,
workInProgress,
type,
newProps,
rootContainerInstance,
);
// @todo: 后续理解
if (current.ref !== workInProgress.ref) {
markRef(workInProgress);
}
}else{
// 找到当前的 Context 环境
const currentHostContext = getHostContext();
if (wasHydrated) {
/** @todo: SSR 逻辑暂不看 */
}else{
// 生成 DOM 结构
const instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 插入 DOM 结构中
appendAllChildren(instance, workInProgress, false, false);
// 把 DOM 结构缓存在 Fiber 上
workInProgress.stateNode = instance;
if(
finalizeInitialChildren(
instance,
type,
newProps,
rootContainerInstance,
currentHostContext,
)
){
// 标记需要更新节点
markUpdate(workInProgress);
}
}
if (workInProgress.ref !== null) {
// 标记需要处理 ref
markRef(workInProgress);
}
}
return null;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
上面中用到了两个函数,用于操作 flags
,后续将此部分统一放到 ReactFiberTag
那篇文章中。
function markUpdate(workInProgress) {
workInProgress.flags |= Update;
}
function markRef(workInProgress) {
workInProgress.flags |= Ref;
{
workInProgress.flags |= RefStatic;
}
}
2
3
4
5
6
7
8
9
10
对于其中的 mount
阶段核心函数:
- createInstance:构建节点。
- appendAllChildren:
# completeWork
返回值
对于大部分 complteWork
的返回值均为 null
只有少数几个 case
会返回 Fiber
结构,错误处理或 SuspendCompnent
处理。
case SuspenseComponent:
....
if ((workInProgress.flags & DidCapture) !== NoFlags) {
// Something suspended. Re-render with the fallback children.
workInProgress.lanes = renderLanes;
return workInProgress;
}
case SuspenseListComponent:
....
return workInProgress.child;
2
3
4
5
6
7
8
9
10
11
# completeUnitOfWork
跳出逻辑
- 同步逻辑代码:
最外层代码逻辑:只要 workInProgress
不为 null
会一直触发 performUnitOfWork
注:这里
workInProgress
一直是一个全局变量,一般在代码中处理workInprogress
会先拷贝一份再处理。
function workLoopSync() {
while (workInProgress !== null) {
// render => render => completeWork => render => completeWork => completeWork
performUnitOfWork(workInProgress);
}
}
2
3
4
5
6
performUnitOfWork
逻辑
此函数是 reconciler
协调器的核心循环逻辑,beiginWork
是除 fiberRoot
外节点的核心代码,只要 workInProgress.child
能被成功创建,beginWork
函数就支持一直递归循环下去。
beginWork
的截止条件,会进入 completeWork
的处理逻辑。
function performUnitOfWork(unitOfWork: Fiber): void {
// unitOfWork 就是 workInProgress
const current = unitOfWork.alternate;
let next;
// 创建并返回 workInProgress.child
next = beginWork(current, unitOfWork, subtreeRenderLanes);
// 将当前的 workInProgress 的 props 缓存在 `memoziedProps` 上
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// beginWork => completeWork
completeUnitOfWork(unitOfWork);
} else {
// beginWork => beiginWork(next节点)
workInProgress = next;
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
completeWork
的退出条件,就是查看是否一直向上遍历,直至到根节点,此时 workInProgress
为空,则跳出 performUnitOfWOrk
的处理逻辑。