/* 子组件2 */
function Child2(){
return <div>子组件 2</div>
}
/* 子组件1 */
function Child1(){
const [ num , setNumber ] = useState(0)
return <div>
子组件 {num}
<button onClick={() => setNumber(num+1)} >按钮1</button>
</div>
}
/* 父组件 */
export default function Index(){
const [ num , setNumber ] = useState(0)
return <div>
<p>父组件 {num} </p>
<Child1 />
<Child2 />
<button onClick={()=> setNumber(num+1)} >按钮2</button>
</div>
}
点击child1的按钮1触发click事件,setNumber会执行scheduleUpdateOnFiber触发更新
fiber是child1, lane是2
之后进入markRootUpdated(root, lane)将FiberRootNode.pendingLanes = 2
addFiberToLanesMap(root, fiber, lane);将child fiber 存入将FiberRootNode.pendingUpdatersLaneMap
然后是ensureRootIsScheduled(root);
会在微任务阶段执行更新任务
还是在宏任务继续执行click的回调,把dispatchQueue执行完后,会进入flushSyncWorkAcrossRoots_impl(true)
由于onlyLegacy = true,没有进入performSyncWorkOnRoot
再把currentUpdatePriority还原,等宏任务执行完了就执行微任务
scheduleTaskForRootDuringMicrotask会判断是否是Concurrent模式还是legacy,这里是legacy模式,继续往下执行flushSyncWorkAcrossRoots_impl(false),这里就会进入到performSyncWorkOnRoot
nextLanes = 2
performSyncWorkOnRoot主要是处理render和commit,并且在最后会又执行一次ensureRootIsScheduled
render:
commit:
commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions, workInProgressDeferredLane);
renderRootSync
function renderRootSync(root, lanes) {
movePendingFibersToMemoized(root, lanes); // 将pendingUpdatersLaneMap存放的child1 fiber放入memoizedUpdaters
prepareFreshStack(root, lanes); // 更新workInProgress
// finishQueueingConcurrentUpdates 更新fiber的childLanes
workLoopSync() // 递归处理fiber,如果没有处理的fiber,
}
function finishQueueingConcurrentUpdates() { // setNumber会将dispatch放入concurrentQueues
var endIndex = concurrentQueuesIndex;
concurrentQueuesIndex = 0;
concurrentlyUpdatedLanes = NoLanes;
var i = 0;
while (i < endIndex) {
var fiber = concurrentQueues[i];
concurrentQueues[i++] = null;
var queue = concurrentQueues[i];
concurrentQueues[i++] = null;
var update = concurrentQueues[i];
concurrentQueues[i++] = null;
var lane = concurrentQueues[i];
concurrentQueues[i++] = null;
if (queue !== null && update !== null) {// update处理成循环队列
var pending = queue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
}
if (lane !== NoLane) {
markUpdateLaneFromFiberToRoot(fiber, update, lane);// child1 fiber往上的parent.childLanes = 2,alternate也更新
}
}
}
从root开始遍历fiber
function workLoopSync() {
// Perform work without checking if we need to yield between fiber.
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
function performUnitOfWork(unitOfWork) {
// 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.
var current = unitOfWork.alternate;
setCurrentFiber(unitOfWork);
var next;
if ((unitOfWork.mode & ProfileMode) !== NoMode) {
startProfilerTimer(unitOfWork);
next = beginWork(current, unitOfWork, entangledRenderLanes);
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
} else {
next = beginWork(current, unitOfWork, entangledRenderLanes);
}
resetCurrentFiber(); // 这个fiber处理完成了
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// If this doesn't spawn new work, complete the current work.
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
ReactCurrentOwner$1.current = null;
}
function beginWork$1(current, workInProgress, renderLanes) {
{
if (workInProgress._debugNeedsRemount && current !== null) {
return remountFiber(current, workInProgress, createFiberFromTypeAndProps(workInProgress.type, workInProgress.key, workInProgress.pendingProps, workInProgress._debugSource || null, workInProgress._debugOwner || null, workInProgress.mode, workInProgress.lanes));
}
}
if (current !== null) {
var oldProps = current.memoizedProps;
var newProps = workInProgress.pendingProps;
if (oldProps !== newProps || hasContextChanged() || (
workInProgress.type !== current.type )) {
didReceiveUpdate = true;
} else {
var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(current, renderLanes);
if (!hasScheduledUpdateOrContext &&
(workInProgress.flags & DidCapture) === NoFlags$1) {
didReceiveUpdate = false;
return attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes);
}
if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags$1) {
didReceiveUpdate = true;
} else {
didReceiveUpdate = false;
}
}
} else {
didReceiveUpdate = false;
if (getIsHydrating() && isForkedChild(workInProgress)) {
var slotIndex = workInProgress.index;
var numberOfForks = getForksAtLevel();
pushTreeId(workInProgress, numberOfForks, slotIndex);
}
}
workInProgress.lanes = NoLanes;
switch (workInProgress.tag) {
//......
}
root fiber的props没有发生变化,并且lanes是0,不等于2,会进入attemptEarlyBailoutIfNoScheduledUpdate
function attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes) {
switch (workInProgress.tag) {
// ......
}
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {
if (current !== null) {
// Reuse previous dependencies
workInProgress.dependencies = current.dependencies;
}
{
// Don't update "base" render times for bailouts.
stopProfilerTimerIfRunning();
}
markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work.
if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
// The children don't have any work either. We can skip them.
// TODO: Once we add back resuming, we should check if the children are
// a work-in-progress set. If so, we need to transfer their effects.
{
return null;
}
} // This fiber doesn't have work, but its subtree does. Clone the child
// fibers and continue.
cloneChildFibers(current, workInProgress);
return workInProgress.child;
}
如果fiber.childLanes不在renderLanes里面,则跳过这个fiber
如果是之前markUpdateLaneFromFiberToRoot冒泡到过的fiber,则会进入cloneChildFibers
function cloneChildFibers(current, workInProgress) {
if (current !== null && workInProgress.child !== current.child) {
throw new Error('Resuming work not yet implemented.');
}
if (workInProgress.child === null) {
return;
}
var currentChild = workInProgress.child;
var newChild = createWorkInProgress(currentChild, currentChild.pendingProps); // 如果没有alternate,会新建一个,如果有,则会根据current更新数据,注意newChild.child = currentChild.child
workInProgress.child = newChild;
newChild.return = workInProgress;
while (currentChild.sibling !== null) {
currentChild = currentChild.sibling;
newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps);
newChild.return = workInProgress;
}
newChild.sibling = null;
} // Reset a workInProgress child set to prepare it for a second pass.
经过冒泡的fiber会将child fibers经过更新从alternate迁移过来
之后将child fiber返回,回到performUnitOfWork,重置currentFiber,fiber.memoizedProps = fiber.pendingProps
workLoopSync处理 child fiber
注意cloneChildFibers拷贝当前层的child fiber,拷贝出来的child fiber的child的fiber仍然是current 这边的fiber,等到下一次处理的时候才会变成alternate的fiber。current和alternate的stateNode都指向同一个
beginWork child1
一直处理到child1 fiber,由于child1 fiber.lanes = 2,不会进入attemptEarlyBailoutIfNoScheduledUpdate,在beginWork继续往下走,走到updateFunctionComponent
function updateFunctionComponent(current, workInProgress, Component, nextProps, renderLanes) {
ReactCurrentOwner$2.current = workInProgress;
setIsRendering(true);
nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes);
hasId = checkDidRenderIdHook();
setIsRendering(false);
{
markComponentRenderStopped();
}
if (current !== null && !didReceiveUpdate) {
bailoutHooks(current, workInProgress, renderLanes);
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
if (getIsHydrating() && hasId) {
pushMaterializedTreeId(workInProgress);
} // React DevTools reads this flag.
workInProgress.flags |= PerformedWork;
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) {
var children = Component(props, secondArg);
}
var children = Component(props, secondArg);这步会重新运行Child1组件
const [ num , setNumber ] = useState(0)中num会取update里面最新的值,并把didReceiveUpdate = true
执行完child1后,children就是更新后的结果,可以看到div中的props第二个从0变到了1
之后进入reconcileChildren
reconcileChildren
workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes);
workInProgress指的函数组件
workInProgress.child是函数组件的根元素
nextChildren是指jsx返回的element
reconcileChildren会先构建div fiber,然后将子element 放在peddingprops,之后再
beginWork div fiber之后,由于div的props是新建的,会进入updateHostRoot之后又会进入reconcileChildren和reconcileChildFibers再去reconcileChildrenArray批量构建sibling fiber
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) {
// This indirection only exists so we can reset `thenableState` at the end.
// It should get inlined by Closure.
thenableIndexCounter$1 = 0;
var firstChildFiber = reconcileChildFibersImpl(returnFiber, currentFirstChild, newChild, lanes);
thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets
// set at the beginning.
return firstChildFiber;
}
function reconcileChildFibersImpl(returnFiber, currentFirstChild, newChild, lanes) {
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes));
}
reconcileSingleElement会clone div fiber,将最新的pendingProps赋值给 div workInProgress, 然后child fiber连接div workInProgress
冒泡的fiber在bailoutOnAlreadyFinishedWork clone 更新的fiber在reconcileChildFibers clone
beginWork child1 div
之后performUnitOfWork div alternate,由于发生了变化,didReceiveUpdate = true,进入
updateHostComponent -> reconcileChildFibersImpl
由于div.child是数组,进入reconcileChildrenArray
reconcileChildrenArray中会进行updateTextNode对这些文本fiber进行更新(同样会进行clone)
beginWork child1 div '子组件'
由于lanes = 0,其实更新child div的时候也已经更新过了,返回null
completeWork child1 div '子组件'
进入到completeUnitOfWork completeWork
'子组件' complete之后,workInProgress指向当前fiber的sibling,也就是num
completeWork child1 div 'num'
在div的reconcileChildrenArray的时候,其child fiber都进行了clone,因此num fiber的pendingProps就在这时候变成了1
beginWork会进入updateHostText 返回null
unitOfWork.memoizedProps = unitOfWork.pendingProps;
进入completeWork
function updateHostText(current, workInProgress, oldText, newText) {
{
// If the text differs, mark it as an update. All the work in done in commitWork.
if (oldText !== newText) {
markUpdate(workInProgress);
}
}
}
function markUpdate(workInProgress) {
workInProgress.flags |= Update;
}
Update = 4
num fiber flag被标记为了4
之后workInProgress指向button
completeWork child1 div button
进入completeWork
因为child1重新执行,所以current fiber 和 workInProgress 绑定的事件函数不是同一个,因此也被标记flag = 4
由于button没有sibling,workInProgress取div
completeWork child1 div
div的props由['子组件','0', button] => ['子组件','1', button] 发生了变化,也被打上flag=4的标记
workInProgress 取 child1
completeWork child1
type为函数直接继续冒泡,没有更新,没有标记
workInProgress 取 child2
beginWork child2
因为child2.childlanes = 0 没有被冒泡到,所以beginWork返回null
completeWork child2
type为函数直接继续冒泡,没有更新,没有标记
workInProgress 取 index button
beginWork button
completeWork button
......