1 )概述
performUnitOfWork
里面的 beginWork
阶段performUnitOfWork
里面,还有一个方法叫做 completeUnitOfWork
completeUnitOfWork
中会根据是否有中断,调用不同的一个处理方法renderRoot
当中, 使用try catch
去调用 workLoop
去循环每一个节点的workLoop
里面调用的就是 performUnitOfWork
performUnitOfWork
里面调用了 beginWork
completeUnitOfWork
,是处理到一侧的子节点遍历到最下层的时候,它没有子节点可以返回了beginWork
里面调用了各种update,它们都是return自己的childRootFiber
执行了 beginWork
,然后执行了之后 return 的是它的 child,也就是 App
beginWork
completeUnitOfWork
completeUnitOfWork
就是对当前这个input界定 执行它的complete,具体执行什么beginWork
的过程当中,如果我们去update某一个节点的时候,有报错了renderRoot
当中,我们可以看到它如果有报错,它的catch是要在 renderRoot
里面被捕获的这个错误while
循环当中,它里面并没有 break
packages/react-reconciler/src/ReactFiberScheduler.js#L1276
completeUnitOfWork
的阶段,会对有各种不同标记的一个节点,执行不同的方法completeWork
这么一个方法unwindWork
的方法completeUnitOfWork
之后,它没有兄弟节点completeUnitOfWork
List
节点是没有任何更新的, 这个时候我们就会返回它的兄弟节点 List
beginWork
的一个过程completeUnitOfWork
里面非常重要的一个判断条件
beginWork
和 completeUnitOfWork
,对每一个节点的更新过程标记了completeUnitOfWork
里面会完成这一步,把所有的effect节点进行一个串联commitWork
的阶段可以非常方便的根据这个链去执行每一个节点的最终的操作2 ) 源码
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L939
找到 completeUnitOfWork
// 这个API 是在 workInProgress 的基础上进行的一个操作
function completeUnitOfWork(workInProgress: Fiber): Fiber | null {
// Attempt to complete the current unit of work, then move to the
// next sibling. If there are no more siblings, return to the
// parent fiber.
// 进来首先就是一个 while true 的一个循环
while (true) {
// The current, flushed, state of this fiber is the alternate.
// Ideally nothing should rely on this, but relying on it here
// means that we don't need an additional field on the work in
// progress.
// 对于这个循环, 它首先获取current
const current = workInProgress.alternate;
if (__DEV__) {
ReactCurrentFiber.setCurrentFiber(workInProgress);
}
// 然后 获取 returnFiber 和 siblingFiber,也就是它的父节点以及它的兄弟节点
// 这个在后面我们判断是否要返回兄弟节点的时候就会用到
const returnFiber = workInProgress.return;
const siblingFiber = workInProgress.sibling;
// 这里首先一进来就有一个大的判断,它是一个整个方法里面最大的一个判断
// Incomplete 就是这个节点它是出现了错误,然后被捕获的, 并标记这个 sideEffect
// 逻辑与操作来判断某一个属性上面它是否有某一个特性的一个方式
if ((workInProgress.effectTag & Incomplete) === NoEffect) {
if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
// Don't replay if it fails during completion phase.
mayReplayFailedUnitOfWork = false;
}
// This fiber completed.
// Remember we're completing this unit so we can find a boundary if it fails.
nextUnitOfWork = workInProgress;
if (enableProfilerTimer) {
if (workInProgress.mode & ProfileMode) {
startProfilerTimer(workInProgress);
}
nextUnitOfWork = completeWork(
current,
workInProgress,
nextRenderExpirationTime,
);
if (workInProgress.mode & ProfileMode) {
// Update render duration assuming we didn't error.
stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
}
} else {
nextUnitOfWork = completeWork(
current,
workInProgress,
nextRenderExpirationTime,
);
}
if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
// We're out of completion phase so replaying is fine now.
mayReplayFailedUnitOfWork = true;
}
stopWorkTimer(workInProgress);
resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
if (__DEV__) {
ReactCurrentFiber.resetCurrentFiber();
}
if (nextUnitOfWork !== null) {
// Completing this fiber spawned new work. Work on that next.
return nextUnitOfWork;
}
// 接下去这个个判断,是要构造一个所有 有 SideEffect 的节点的一个链状的结构
// 这个链状结构最终是用于 commitWork 的时候用来进行对这些有 SideEffect的节点进行 commit 的一个操作,
// 这边它的一个判断条件,returnFiber不等于null,并且returnFiber它不是一个 Incomplete 的一个节点
// 因为对于一个 Incomplete 的节点,它唯一可以具有的一个SideEffect,就是这个节点已经被捕获了
// 因为对于有 Incomplete 错误的节点是不会渲染正常的子节点的
if (
returnFiber !== null &&
// Do not append effects to parents if a sibling failed to complete
(returnFiber.effectTag & Incomplete) === NoEffect
) {
// Append all the effects of the subtree and this fiber onto the effect
// list of the parent. The completion order of the children affects the
// side-effect order.
// 对于正常的一个情况, 首先要判断一下 returnFiber.firstEffect 是否等于 null
// 符合判断就代表 现在这个 returnFiber 上还没有记录任何它的子节点的有副作用的子节点
// 这个时候, 直接把当前节点的firstEffect赋值给 returnFiber.firstEffect
// 因为它之前是没有任何一个的嘛,我们这边真正要做的是把当前节点的 firstEffect 到 lastEffect的一个链条
// 这个单项链表,给它挂载到它的父节点的同样的一个 firstEffect到lastEffect的单项链表的最后
if (returnFiber.firstEffect === null) {
returnFiber.firstEffect = workInProgress.firstEffect;
}
// 就是下面这一段判断,就是来做这个事情的, 如果returnFiber.lastEffect不等于null,那说明它已经有了
// 那么对于returnFiber上面有记录过别的 SideEffect 的节点之后
// 我们当前节点是挂载到整个 SideEffect 链的最后,就是下面这样,就是把它连到最后的上面
if (workInProgress.lastEffect !== null) {
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
}
// 还要操作如下,因为它的这个(returnFiber.lastEffect)指针目前还指向它原来的那个lastEffect
// 在 赋值 nextEffect之后,它的最后一个就是这个链的最后一个已经变成 workInProgress.lastEffect,所以这边要执行这么一个操作
// 当然这个条件是要建立在 workInProgress.lastEffect 是有值的情况
// 这是把它们各自的firsteffect到lasteffect,这个链给它进行一个串联的过程
returnFiber.lastEffect = workInProgress.lastEffect;
}
// If this fiber had side-effects, we append it AFTER the children's
// side-effects. We can perform certain side-effects earlier if
// needed, by doing multiple passes over the effect list. We don't want
// to schedule our own side-effect on our own list because if end up
// reusing children we'll schedule this effect onto itself since we're
// at the end.
// 对于returnFiber来说,当前这个节点也可能是有副作用的,那么这边就接下去就会做这个操作
// 如果当前节点的 effectTag > PerformedWork 的,因为 PerformedWork 是一个给 DEVTool 用的一个 sideEffect
// 对于真正的react更新是没有任何意义的, 所以如果它仅仅只有 PerformedWork ,它就不是一个有效的 SideEffect 的节点
const effectTag = workInProgress.effectTag;
// Skip both NoWork and PerformedWork tags when creating the effect list.
// PerformedWork effect is read by React DevTools but shouldn't be committed.
if (effectTag > PerformedWork) {
// 如果它有 SideEffect,就把当前节点作为父节点的 SideEffect 链的最后一个给它挂载上去
// 或者如果是当前父节点没有任何记录的 SideEffect,它就是第一个
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = workInProgress;
} else {
returnFiber.firstEffect = workInProgress;
}
returnFiber.lastEffect = workInProgress;
}
}
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
}
if (siblingFiber !== null) {
// If there is more work to do in this returnFiber, do that next.
return siblingFiber;
} else if (returnFiber !== null) {
// If there's no more work in this returnFiber. Complete the returnFiber.
workInProgress = returnFiber;
continue;
} else {
// We've reached the root.
return null;
}
} else {
if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
// Record the render duration for the fiber that errored.
stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
// Include the time spent working on failed children before continuing.
let actualDuration = workInProgress.actualDuration;
let child = workInProgress.child;
while (child !== null) {
actualDuration += child.actualDuration;
child = child.sibling;
}
workInProgress.actualDuration = actualDuration;
}
// This fiber did not complete because something threw. Pop values off
// the stack without entering the complete phase. If this is a boundary,
// capture values if possible.
const next = unwindWork(workInProgress, nextRenderExpirationTime);
// Because this fiber did not complete, don't reset its expiration time.
if (workInProgress.effectTag & DidCapture) {
// Restarting an error boundary
stopFailedWorkTimer(workInProgress);
} else {
stopWorkTimer(workInProgress);
}
if (__DEV__) {
ReactCurrentFiber.resetCurrentFiber();
}
if (next !== null) {
stopWorkTimer(workInProgress);
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
}
// If completing this work spawned new work, do that next. We'll come
// back here again.
// Since we're restarting, remove anything that is not a host effect
// from the effect tag.
next.effectTag &= HostEffectMask;
return next;
}
if (returnFiber !== null) {
// Mark the parent fiber as incomplete and clear its effect list.
returnFiber.firstEffect = returnFiber.lastEffect = null;
returnFiber.effectTag |= Incomplete;
}
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
}
// 存在 sibling 节点, 注意这边是return,也就是跳出while循环
if (siblingFiber !== null) {
// If there is more work to do in this returnFiber, do that next.
return siblingFiber;
} else if (returnFiber !== null) {
// If there's no more work in this returnFiber. Complete the returnFiber.
// 如果returnFiber不等于null,那么 workInProgress = returnFiber
// 比如对于最上面示例图的 input节点,它的 completeUnitOfWork
// 如果它没有兄弟节点,那么它就继续执行Input的 completeUnitOfWork
// 这个循环是在 completeUnitOfWork 内部进行的一个过程
workInProgress = returnFiber;
continue;
} else {
// 如果 returnFiber 也等于null,那么它直接 return null
// 说明已经到达顶点了,就到达 RootFiber 了,我们的更新过程呢已经完成了
// 只需要在接下去 commitRoot 就可以了
return null;
}
}
}
// Without this explicit null return Flow complains of invalid return type
// TODO Remove the above while(true) loop
// eslint-disable-next-line no-unreachable
return null;
}
import React, { Component } from 'react'
import './App.css'
class List extends Component {
state = {
a: 1,
b: 2,
c: 3,
}
handleClick = () => {
this.setState(oldState => {
const { a, b, c } = oldState
return {
a: a * a,
b: b * b,
c: c * c,
}
})
}
render() {
const { a, b, c } = this.state
return [
<span key="a">{a}</span>,
<span key="b">{b}</span>,
<span key="c">{c}</span>,
<button key="button" onClick={this.handleClick}>
click me
</button>,
]
}
}
class Input extends Component {
state = {
name: 'wang',
}
handleChange = e => {
// 这里如果使用方法设置`state`
// 那么需要现在外面读取`e.target.value`
// 因为在React走完整个事件之后会重置event对象
// 以复用event对象,如果等到方法被调用的时候再读取`e.target.value`
// 那时`e.target`是`null`
this.setState({
name: e.target.value,
})
}
render() {
return (
<input
type="text"
style={{ color: 'red' }}
onChange={this.handleChange}
value={this.state.name}
/>
)
}
}
class App extends Component {
render() {
return (
<div className="main">
<Input />
<List />
</div>
)
}
}
export default App
updateQueue: ["children", "4"]
updateQueue: ["children", "9"]
,也就是原值为 3 的节点v16.6.3/packages/react-reconciler/src/ReactFiberScheduler.js#L1026
这个 if 里面被实现的Update
对应值为4 (二进制的表示形式, 参考 ReactSideEffectTags.js),它们要把内容挂载到dom上面completeUnitOfWork
的是 第一个 span, 接下去它的 siblingif (returnFiber.firstEffect === null) {
returnFiber.firstEffect = workInProgress.firstEffect;
}
Update
, 值为 4 肯定是大于 Performedwork
值为 1的if (effectTag > PerformedWork) {
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = workInProgress; // 情况1,标记为 L1, 以便下面引用说明
} else {
returnFiber.firstEffect = workInProgress; // 情况2, 标记为 L2
}
returnFiber.lastEffect = workInProgress; // 通用设定3,标记为 L3
}
L2
和 L3
L1
completeUnitOfWork
的时候,自己没有任何的更新completeUnitOfWork
的时候, 代码中的 returnFiber 对应是 divRootFiber
上面记录的completeUnitOfWork
,最终能够赋值到 RootFiber
上面