之前曾经写过一篇介绍fiber的文章,当时提到fiber就是一个实现了时间分片+链表结构的数据结构。这里结合源码介绍下fiber的链表结构是如何工作的。
reconciliation/render阶段
react(v16.6)实现reconciliation/伪代码, 源码位置1
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189/*
React的reconciliation过程其实就是执行一个大的while循环,完成树的遍历,搜集副作用effect
主要函数有:
completeUnitOfWork, performUnitOfWork 用于完成树的循环遍历
beginWork, completeWork 具体执行每个节点的处理工作, 更新当前节点的状态,比如context,props,state等等
nexUnitOfWork 总是指向当前正在工作的单元
*/
// 主流程:renderRoot -> workLoop -> performUnitOfWork -> beginWork -> completeWork
function workLoop(isYieldy) {
if (!isYieldy) {
// Flush work without yielding
while (nextUnitOfWork !== null) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
} else {
// Flush asynchronous work until there's a higher priority event
while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}
}
function performUnitOfWork(workInProgress) {
// 当前fiber节点的备胎,以便有错误时可以回滚
const current = workInProgress.alternate;
next = beginWork(current, workInProgress, nextRenderExpirationTime);
workInProgress.memoizedProps = workInProgress.pendingProps;
// 没有子节点,说明已经来到了分支末尾,查看同级是否有兄弟节点,执行处理,完成后回溯到父节点
if (next === null) {
next = completeUnitOfWork(workInProgress);
}
// 将子节点作为下一个工作单元
return next;
}
// 返回nextUnitOfWork, 一般是workInProgress下的第一个子节点
function beginWork(current, workInProgress, renderExpirationTime) {
// 可以理解为下面这一大坨都最终执行了 return workInProgress.child;
// 根据fiber.alternate(current)是否有值来判断是mount还是update
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
oldProps === newProps &&
!hasLegacyContextChanged() &&
updateExpirationTime < renderExpirationTime
) {
switch (workInProgress.tag) {
case ClassComponent:
//...
break;
case FunctionComponent:
//...
break;
}
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderExpirationTime);
}
}
// Before entering the begin phase, clear the expiration time.
workInProgress.expirationTime = NoWork;
switch (workInProgress.tag) {
case ClassComponent:
return updateClassComponent();
case FunctionComponent:
return updateFunctionComponent();
//...
}
}
function completeUnitOfWork(workInProgress) {
while (true) {
let returnFiber = workInProgress.return;
let siblingFiber = workInProgress.sibling;
nextUnitOfWork = completeWork(workInProgress);
// 如果有兄弟节点,返回上层函数,将兄弟节点作为下一个工作单元
if (siblingFiber !== null) {
// If there is a sibling, return it
// to perform work for this sibling
return siblingFiber;
// 如果没有兄弟节点,说明同级的子节点都已经处理完,如果父节点存在,回到循环开始处,继续完成父节点的工作
} else if (returnFiber !== null) {
// If there's no more work in this returnFiber,
// continue the loop to complete the parent.
workInProgress = returnFiber;
continue;
} else {
// 回到fiber树根节点
return null;
}
}
}
function completeWork(current, workInProgress, renderExpirationTime) {
switch (workInProgress.tag) {
//...
}
return null;
}
// reconciliation/render阶段的入口方法,生成当前组件子节点的虚拟DOM, 组件实例与真实DOM, 收集effectTag
function renderRoot(root, isYieldy) {
flushPassiveEffects();
const expirationTime = root.nextExpirationTimeToWorkOn;
// 检查是开始一个新栈还是恢复之前暂停的工作
if (
expirationTime !== nextRenderExpirationTime ||
root !== nextRoot ||
nextUnitOfWork === null
) {
// Reset the stack and start working from the root.
resetStack();
nextRoot = root;
nextRenderExpirationTime = expirationTime;
nextUnitOfWork = createWorkInProgress(
nextRoot.current,
null,
nextRenderExpirationTime,
);
root.pendingCommitExpirationTime = NoWork;
}
do {
try {
workLoop(isYieldy);
} catch (throwValue) {
// onUncaughtError(thrownValue);
if (nextUnitOfWork === null) {
// This is a fatal error.
didFatal = true;
onUncaughtError(thrownValue);
} else {
const sourceFiber = nextUnitOfWork;
let returnFiber = sourceFiber.return;
if (returnFiber === null) {
// 根节点捕获了错误,致命的,必须停止工作
didFatal = true;
onUncaughtError(thrownValue);
} else {
throwException();
nextUnitOfWork = completeUnitOfWork(sourceFiber);
continue;
}
}
}
break;
} while (true);
// We're done performing work. Time to clean up.
isWorking = false;
ReactCurrentOwner.currentDispatcher = null;
// Yield back to main thread.
if (didFatal) {
// nextRoot指向当前正在工作的根节点,非空代表正处于异步渲染过程中,这里设置为null,表明当前批次中无其他工作可做
nextRoot = null;
return;
}
// 这棵树上还有剩余异步工作要做,但是当前帧中已经没有时间. 除非被一个更高优先级的更新打断,否则稍后从我们离开的地方继续执行
if (nextUnitOfWork !== null) {
onYield(root);
return;
}
// 做完了整棵树, 此处的rootWorkInProgress就是在commitRoot中的finishedWork
const rootWorkInProgress = root.current.alternate;
nextRoot = null;
// 这棵树被挂起了,suspended
if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {
onSuspend();
return;
}
// Ready to commit.
onComplete(root, rootWorkInProgress, expirationTime);
}
来一个简单模拟,结合下文可以更好的理解上面workLoop的工作流程1
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95/**
* 模拟Fiber的链表结构,tiny实现reconciliation中的工作循环
*/
// 假设一颗dom tree
const a1 = {name: 'a1'};
const b1 = {name: 'b1'};
const b2 = {name: 'b2'};
const b3 = {name: 'b3'};
const c1 = {name: 'c1'};
const c2 = {name: 'c2'};
const d1 = {name: 'd1'};
const d2 = {name: 'd2'};
a1.render = () => [b1, b2, b3];
b1.render = () => [];
b2.render = () => [c1];
b3.render = () => [c2];
c1.render = () => [d1, d2];
c2.render = () => [];
d1.render = () => [];
d2.render = () => [];
// 定义fiber结构
class Fiber{
constructor(instance) {
this.instance = instance;
this.child = null;
this.sibling = null;
this.return = null;
}
}
// 将组件实例ReactElement转换为fiber
function createFiberTree(root, children) {
if (!children) {
return null;
}
root.child = children.reduceRight(function(pre, current){
const fiber = new Fiber(current);
fiber.sibling = pre;
fiber.return = root;
return fiber;
}, null);
return root.child;
}
// 建立当前节点和子节点的关系 beginWork实现,完成当前fiber要做的工作,总是返回循环处理中下一个工作的子节点或null
function doWork(node) {
console.log('dowork, current fiber node is', node.instance.name);
const children = node.instance.render();
return createFiberTree(node, children);
}
// 从根节点开始,构造fiber链表 先序遍历+深搜
function workLoop(node) {
let root = node;
let current = node;
while (true) {
// 总是得到当前节点的第一个子节点或null
let child = doWork(current);
// 如果有子节点,则继续处理子节点
if (child) {
current = child;
continue;
}
if (current === root) {
return;
}
// 没有子节点,处理兄弟节点
// completeUnitOfWork实现
// 如果当前节点已经没有兄弟节点, 说明父节点下所有节点都已处理完毕,需要向上回溯继续处理父节点的兄弟节点
while (!current.sibling) {
if (current.return === root || !current.return) {
return;
}
// 回溯到父节点继续处理
current = current.return;
}
// 如果有兄弟节点,则处理兄弟节点
current = current.sibling;
}
}
const rootFiber = new Fiber(a1);
workLoop(rootFiber);
commit阶段
1 | // commit阶段的入口方法,更新DOM、ref等, 执行相应组件钩子 |
时序调度
调度的核心方法执行过程为:1
2// 调度的目的主要是为了知道哪个节点优先执行,什么时候执行
scheduleWork -> requestWork -> performWorkOnRoot -> performWorkOnRoot -> (completeRoot) -> renderRoot -> commitRoot
1 | // ReactDOM.render,setState,forceUpdate都要经过这里 |