fiber是如何工作的

之前曾经写过一篇介绍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
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
// commit阶段的入口方法,更新DOM、ref等, 执行相应组件钩子
function commitRoot(root, finishedWork) {

// willUpdate
commitBeforeMutationLifecycles()

// dom更新
commitAllHostEffects();
root.current = finishedWork;

// didMount/didUpdate
commitAllLifeCycles();
}

// 源码分析
function commitRoot(root, finishedWork){
isWorking = true;
isComitting = true;
root.pendingCommitExpirationTime = NoWork;

// 在调用生命周期钩子前更新当前正在提交工作的优先级
const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;
const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;
const earliestRemainingTimeBeforeCommit =
childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit
? childExpirationTimeBeforeCommit
: updateExpirationTimeBeforeCommit;
markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);

// 调用钩子前重置current为null
ReactCurrentOwner.current = null;

let firstEffect;

// 根节点有副作用
if (finishedWork.effectTag > PerformedWork) {
// 一个fiber节点的副作用列表仅仅包含它的子节点,不包括其本身。如果root有副作用,需要把它添加到列表尾部。
if (finishedWork.lastEffect !== null) {
finishedWork.lastEffect.nextEffect = finishedWork;
firstEffect = finishedWork.firstEffect
} else {
firstEffect = finishedWork;
}
} else {

// 根节点上没有副作用
firstEffect = finishedWork.firstEffect;
}

// 在突变之前调用getSnapshotBeforeUpdate的钩子。
nextEffect = firstEffect;
while (nextEffect != null) {
let didError = false;
let error;
try {
// commit前执行钩子
commitBeforeMutationLifecycles();
} catch (e) {
didError = true;
error = e;
}
if (didError) {

// 捕获commit阶段的错误
captureCommitPhaseError(nextEffect, error);
// clean-up
if (nextEffect != null) {
nextEffect = nextEffect.nextEffect;
}
}
}

// 在一棵树上commit所有的副作用。这个操作分为两个阶段
// 第一个阶段执行所有host类型节点的插入,更新,删除和ref卸载。
nextEffect = firstEffect;
while (nextEffect !== null) {
let didError = false;
let error;
try {
// 执行host类型的副作用,包括dom更新,增删等
commitAllHostEffects();
} catch (e) {
didError = true;
error = e;
}
if (didError) {

// 捕获commit阶段的错误
captureCommitPhaseError(nextEffect, error);
if (nextEffect != null) {
nextEffect = nextEffect.nextEffect;
}
}
}

resetAfterCommit(root.containerInfo);

// 在第一阶段执行完后,才设置work-in-progress树为当前current树。这样可以保证在执行willUnmount期间,current树仍然是之前
// 的树;而在第二阶段开始后,didMount和didUpdate期间当前树是finishedWork
root.current = finishedWork;

// 第二阶段React会执行所有的生命周期钩子和ref回调。
// 这些方法都被单独调用以便入口树上所有的替换,更新,删除操作都已经被执行。
// 这个阶段还会触发任意的渲染器上具体的初始化副作用
nextEffect = firstEffect;
while (nextEffect !== null) {
let didError = false;
let error;
try {
commitAllLifeCycles(root, committedExpirationTime);
} catch (e) {
didError = true;
error = e;
}
if (didError) {

// 捕获commit阶段的错误
captureCommitPhaseError(nextEffect, error);
if (nextEffect != null) {
nextEffect = nextEffect.nextEffect;
}
}
}

isCommitting = false;
isWorking = false;
onCommitRoot(finishedWork.stateNode);

const updateExpirationTimeAfterCommit = finishedWork.expirationTime;
const childExpirationTimeAfterCommit = finishedWork.childExpirationTime;
const earliestRemainingTimeAfterCommit =
childExpirationTimeAfterCommit > updateExpirationTimeAfterCommit
? childExpirationTimeAfterCommit
: updateExpirationTimeAfterCommit;
if (earliestRemainingTimeAfterCommit === NoWork) {
// If there's no remaining work, we can clear the set of already failed
// error boundaries.
legacyErrorBoundariesThatAlreadyFailed = null;
}
onCommit(root, earliestRemainingTimeAfterCommit);
}

时序调度

调度的核心方法执行过程为:

1
2
// 调度的目的主要是为了知道哪个节点优先执行,什么时候执行
scheduleWork -> requestWork -> performWorkOnRoot -> performWorkOnRoot -> (completeRoot) -> renderRoot -> commitRoot

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
// ReactDOM.render,setState,forceUpdate都要经过这里
function scheduleWork(fiber, expirationTime) {
// 将当前fiber及其alternate的过期时间加大,提高优先级
const root = scheduleWorkToRoot(fiber, expirationTime);
if (root === null) {
return;
}

if (
!isWorking &&
nextRenderExpirationTime !== NoWork &&
expirationTime > nextRenderExpirationTime
) {
resetStack();
}
markPendingPriorityLevel(root, expirationTime);
if (
// If we're in the render phase, we don't need to schedule this root
// for an update, because we'll do it before we exit...
!isWorking ||
isCommitting ||
// ...unless this is a different root than the one we're rendering.
nextRoot !== root
) {
const rootExpirationTime = root.expirationTime;
requestWork(root, rootExpirationTime);
}
}

// 决定通过什么方式来进入performWorkOnRoot
function requestWork(root, expirationTime) {
addRootToSchedule(root, expirationTime);

// 如果当前已经开始了渲染,直接返回
if (isRendering) {
// 剩余的工作将在当前渲染批次的末尾被调度
return;
}

// 直接调用performWorkOnRoot
if (isBatchingUpdates) {
// Flush work at the end of the batch.
if (isUnbatchingUpdates) {
// ...unless we're inside unbatchedUpdates, in which case we should
// flush it now.
nextFlushedRoot = root;
nextFlushedExpirationTime = Sync;
performWorkOnRoot(root, Sync, false);
}
return;
}

// performSyncWork -> performSync -> performWork -> performWorkOnRoot
if (expirationTime === Sync) {
performSyncWork();
} else {

// scheduleCallbackWithExpirationTime -> scheduleCallbackWithExpirationTime -> performAsyncWork -> performWork -> performWorkOnRoot
scheduleCallbackWithExpirationTime(root, expirationTime);
}
}

// 判断是同步还是异步渲染 isYieldy
function performWork(minExpirationTime, isYieldy) {
// 找到最高优先级的root
findHighestPriorityRoot();

if (isYieldy) {
recomputeCurrentRendererTime();
currentSchedulerTime = currentRendererTime;

if (enableUserTimingAPI) {
const didExpire = nextFlushedExpirationTime > currentRendererTime;
const timeout = expirationTimeToMs(nextFlushedExpirationTime);
stopRequestCallbackTimer(didExpire, timeout);
}

while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
minExpirationTime <= nextFlushedExpirationTime &&
!(didYield && currentRendererTime > nextFlushedExpirationTime)
) {
performWorkOnRoot(
nextFlushedRoot,
nextFlushedExpirationTime,
currentRendererTime > nextFlushedExpirationTime,
);
findHighestPriorityRoot();
recomputeCurrentRendererTime();
currentSchedulerTime = currentRendererTime;
}
} else {
while (
nextFlushedRoot !== null &&
nextFlushedExpirationTime !== NoWork &&
minExpirationTime <= nextFlushedExpirationTime
) {
performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
findHighestPriorityRoot();
}
}

// We're done flushing work. Either we ran out of time in this callback,
// or there's no more work left with sufficient priority.

// If we're inside a callback, set this to false since we just completed it.
if (isYieldy) {
callbackExpirationTime = NoWork;
callbackID = null;
}
// If there's work left over, schedule a new callback.
if (nextFlushedExpirationTime !== NoWork) {
scheduleCallbackWithExpirationTime(
((nextFlushedRoot: any): FiberRoot),
nextFlushedExpirationTime,
);
}

// Clean-up.
finishRendering();
}

// 判断是直接执行commitRoot 还是先执行renderRoot -> commitRoot
function performWorkOnRoot(root, expirationTime, isYieldy) {
// 判断是异步还是同步工作
if (!isYieldy) {

} else {

}
}
-------------本文结束 感谢您的阅读-------------