createWorkInProgress创建workInPogress Fiber或者复用已有的current Fiber节点
初次渲染

首次执行reactDom.render时,会创建整个应用的根节点,然后FiberRootNode的current指向rootFiber当前应用的根节点。
双缓存机制-首屏渲染的逻辑中,会基于current rootFiber 建立workInProgress rootFiber 。
首屏渲染中,第一次进入createWorkInProgress的current是div #root,还current Fiber没有alternate,此时workInProgress Fiber为null。
createFiber 创建一个新的fiber节点,并将current Fiber的参数都赋值给workInProgress Fiber,并返回workInProgress Fiber
function createWorkInProgress(current, pendingProps) {var workInProgress = current.alternate;if (workInProgress === null) {//createFiber 创建一个新的fiber节点workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);//将current Fiber的参数都赋值给workInProgress FiberworkInProgress.elementType = current.elementType;workInProgress.type = current.type;workInProgress.stateNode = current.stateNode;...//将current指向workInProgress的alternateworkInProgress.alternate = current;//将workInProgress指向current.alternatecurrent.alternate = workInProgress;} else {workInProgress.pendingProps = pendingProps; // Needed because Blocks store data on type.workInProgress.type = current.type; // We already have an alternate.// Reset the effect tag.workInProgress.flags = NoFlags; // The effects are no longer valid.workInProgress.subtreeFlags = NoFlags;workInProgress.deletions = null;{// We intentionally reset, rather than copy, actualDuration & actualStartTime.// This prevents time from endlessly accumulating in new commits.// This has the downside of resetting values for different priority renders,// But works for yielding (the common case) and should support resuming.workInProgress.actualDuration = 0;workInProgress.actualStartTime = -1;}} // Reset all effects except static ones.// Static effects are not specific to a render.//将workInProgress参数赋值为current参数。workInProgress.flags = current.flags & StaticMask;workInProgress.childLanes = current.childLanes;workInProgress.lanes = current.lanes;workInProgress.child = current.child;workInProgress.memoizedProps = current.memoizedProps;workInProgress.memoizedState = current.memoizedState;workInProgress.updateQueue = current.updateQueue; // Clone the dependencies object. This is mutated during the render phase, so// it cannot be shared with the current fiber.var currentDependencies = current.dependencies;workInProgress.dependencies = currentDependencies === null ? null : {lanes: currentDependencies.lanes,firstContext: currentDependencies.firstContext}; // These will be overridden during the parent's reconciliation//将current.sibling赋值workInProgress的sibling等等workInProgress.sibling = current.sibling;workInProgress.index = current.index;workInProgress.ref = current.ref;{workInProgress.selfBaseDuration = current.selfBaseDuration;workInProgress.treeBaseDuration = current.treeBaseDuration;}{workInProgress._debugNeedsRemount = current._debugNeedsRemount;switch (workInProgress.tag) {case IndeterminateComponent:case FunctionComponent:case SimpleMemoComponent:workInProgress.type = resolveFunctionForHotReloading(current.type);break;case ClassComponent:workInProgress.type = resolveClassForHotReloading(current.type);break;case ForwardRef:workInProgress.type = resolveForwardRefForHotReloading(current.type);break;}}return workInProgress;} // Used to reuse a Fiber for a second pass.
在beginWork时,依次执行,执行到div时,由于div是一个HostComponent
执行reconcileChildren前,workInProgress为null
执行完,此时workInProgress.child就是一个新的Fiber节点
执行完reconcileChildren后,将div就有了header节点
整个render执行完之后,就进行commit,将FiberRootNode的current执行workInProgress的RootFiber上,这样workInprogress Fiber树就变成了current Fiber树。
第一次更新
点击p标签,更新时,current指向的是右边的rootFiber,前文中,current.alternate指向的是左边workInProgress的rootFiber。因此workInProgress rootFiber不为null,会复用workInProgress节点。
以app下的子节点div举例,初次渲染是,不存在current节点,但是更新时,同时存在current与workInProgress节点。
function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {if (current === null) {workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);} else {workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes);}}
当执行到div这个节点时,workInProgress的rootFiber、APP节点已经创建完成。
执行reconcileChildFibers,创建workInProgress div的child时,会复用左边current的一些属性,包括header子节点。因此workInProgress div有了自己的子节点,当执行reconcileChildren时,header会创建自己的子节点。

当所有的都渲染完成以后,FiberRootNode的current指针,指向左边的rootFiber。
第二次更新
第二次更新与第一次的区别?
根据流程图,第二次更新时,每一个current节点,都已经存在了对应的workInProgress的alternate指针。(这就是双缓存机制的原理)
“递”阶段update流程 -beginWork
第一个进入beginWork的tag为3,是rootFIber
function beginWork(current, workInProgress, renderLanes) {{...if (current !== null) {var oldProps = current.memoizedProps;var newProps = workInProgress.pendingProps;//根据新旧props,context,type是否相同,来判断这个节点是否有变化,if (oldProps !== newProps || hasContextChanged() || ( // Force a re-render if the implementation changed due to hot reload:workInProgress.type !== current.type )) {// 标记这个节点是否有变化didReceiveUpdate = true;} else {//此段代码表示本次更新中是否有任务,当前rootFiber没有任务,因此didReceiveUpdate为false,//并进入attemptEarlyBailoutIfNoScheduledUpdate,不再像首屏渲染时,进入Fiber节点的Update逻辑var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(current, renderLanes);if (!hasScheduledUpdateOrContext && // If this is the second pass of an error or suspense boundary, there// may not be work scheduled on `current`, so we check for this flag.(workInProgress.flags & DidCapture) === NoFlags) {// No pending updates or context. Bail out now.didReceiveUpdate = false;return attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes);}if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {// This is a special case that only exists for legacy mode.// See https://github.com/facebook/react/pull/19216.didReceiveUpdate = true;} else {// An update was scheduled on this fiber, but there are no new props// nor legacy context. Set this to false. If an update queue or context// consumer produces a changed value, it will set this to true. Otherwise,// the component will assume the children have not changed and bail out.didReceiveUpdate = false;}}} else {didReceiveUpdate = false;...}....//update逻辑}}
当没有任务需要执行时,会直接进入bailoutOnAlreadyFinishedWork,克隆一份workInProgress的child
attemptEarlyBailoutIfNoScheduledUpdate
当前rootFiber没有任务,因此didReceiveUpdate为false,并进入attemptEarlyBailoutIfNoScheduledUpdate,不再像首屏渲染时,进入Fiber节点的Update逻辑
bailoutOnAlreadyFinishedWork
最终进入bailoutOnAlreadyFinishedWork方法
cloneChildFibers
createWorkInProgress
createWorkInProgress创建新的或者复用已有的currentFiber节点,并将这个child作为workInProgress的child。
有任务执行时
继续执行beginWork,当App进入时,跳过attemptEarlyBailoutIfNoScheduledUpdate,继续往下执行
进入updateFunctionComponent的逻辑
updateFunctionComponent

在updateFunctionComponent会调用renderWithHooks的方法
renderWithHooks
会调用Component方法,执行App(),进入App 代码的逻辑,返回jsx对象


此时的child已经返回了App 的jsx对象。
继续执行,renderWithHooks返回给nextChild
reconcileChildren:返回workInProgress的child,基于当前workInProgress Fiber与nextChild的子jsx对象形成一个新的WorkInProgress Fiber。
reconcileChildren
reconcileChildFibers
placeSingleChild:首屏渲染shouldTrackSideEffects为false,更新时为true。
此次更新不会进入里面,因为newFiber,即workInProgress的alternate不为null
什么情况下workInProgress.alternate为null?
由于workInProgress的alternate指向的是current的alternate,而current.alternate为null,表示当前fiber节点在上一个更新中不存在Fiber节点,是在本次更新中新创建的Fiber节点,所以要将新创建的Fiber节点的dom挂载在页面中。
“递”阶段mount与update的区别?
1、在beginWorkwork开始时,中有一个优化的逻辑,如果命中优化的逻辑,会进入bailoutOnAlreadyFinishedWork,克隆一份workInProgress的child。
2、如果没有命中,会继续走不同Fiber节点的Update的逻辑,在Update的逻辑中进入reconcile的逻辑(
reconcileChildren),在这个逻辑中,会将current的Fiber节点与nextChild的Jsx做对比,产生一个新的Fiber节点。
