completeWork
1 )概述
- 在
completeUnitOfWork 当中,在节点是正常渲染没有任何出错的情况下 - 会去调用 completework,对这个节点进行一个完成工作的一系列操作
- 在update各种component的时候,执行了各种获取context相关的内容
- 对于 completeWork,我们会把它对应的这些节点获取的context再pop出来
- 相当于 beginWork是一个正向的流程, 而completeWork 是一个反向的流程
- 正向的流程获取的内容,反向的流程给它依次pop出来
- 最终能够达到一个再被清空的一个流程
- 对于 HostComponent 会执行一个节点的初始化以及更新的一个操作
- 然后会初始化监听事件
2 )源码
定位到 packages/react-reconciler/src/ReactFiberCompleteWork.js#L540
进入
function completeWork( current: Fiber | null, workInProgress: Fiber, renderExpirationTime: ExpirationTime, ): Fiber | null { // 获取当前 props const newProps = workInProgress.pendingProps; // 这里和 beginWork 里面的代码很像 // 在这里面,很多的组件是没有任何的一个工作要做的 // 相对于 class component,它也只需要去pop之前可能push进去过的一些内容 switch (workInProgress.tag) { case IndeterminateComponent: break; case LazyComponent: break; case SimpleMemoComponent: case FunctionComponent: break; case ClassComponent: { const Component = workInProgress.type; if (isLegacyContextProvider(Component)) { popLegacyContext(workInProgress); } break; } // 对于 HostRoot,它也是各种 pop,因为 HostRoot 它会获取很多的一些内容 case HostRoot: { popHostContainer(workInProgress); popTopLevelLegacyContextObject(workInProgress); const fiberRoot = (workInProgress.stateNode: FiberRoot); if (fiberRoot.pendingContext) { fiberRoot.context = fiberRoot.pendingContext; fiberRoot.pendingContext = null; } if (current === null || current.child === null) { // If we hydrated, pop so that we can delete any remaining children // that weren't hydrated. popHydrationState(workInProgress); // This resets the hacky state to fix isMounted before committing. // TODO: Delete this when we delete isMounted and findDOMNode. workInProgress.effectTag &= ~Placement; } // 最终它这边执行了一个叫做 updateHostContainer 这个方法 // 那么这个方法在 react-dom 的环境下面是一个空的方法 updateHostContainer(workInProgress); break; } // 最复杂的是 HostComponent 处理 case HostComponent: { popHostContext(workInProgress); const rootContainerInstance = getRootHostContainer(); const type = workInProgress.type; if (current !== null && workInProgress.stateNode != null) { updateHostComponent( current, workInProgress, type, newProps, rootContainerInstance, ); if (current.ref !== workInProgress.ref) { markRef(workInProgress); } } else { if (!newProps) { invariant( workInProgress.stateNode !== null, 'We must have new props for new mounts. This error is likely ' + 'caused by a bug in React. Please file an issue.', ); // This can happen when we abort work. break; } const currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context // "stack" as the parent. Then append children as we go in beginWork // or completeWork depending on we want to add then top->down or // bottom->up. Top->down is faster in IE11. let wasHydrated = popHydrationState(workInProgress); if (wasHydrated) { // TODO: Move this and createInstance step into the beginPhase // to consolidate. if ( prepareToHydrateHostInstance( workInProgress, rootContainerInstance, currentHostContext, ) ) { // If changes to the hydrated node needs to be applied at the // commit-phase we mark this as such. markUpdate(workInProgress); } } else { let instance = createInstance( type, newProps, rootContainerInstance, currentHostContext, workInProgress, ); appendAllChildren(instance, workInProgress, false, false); // Certain renderers require commit-time effects for initial mount. // (eg DOM renderer supports auto-focus for certain elements). // Make sure such renderers get scheduled for later work. if ( finalizeInitialChildren( instance, type, newProps, rootContainerInstance, currentHostContext, ) ) { markUpdate(workInProgress); } workInProgress.stateNode = instance; } if (workInProgress.ref !== null) { // If there is a ref on a host node we need to schedule a callback markRef(workInProgress); } } break; } // HostText 的处理 case HostText: { let newText = newProps; if (current && workInProgress.stateNode != null) { const oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need // to schedule a side-effect to do the updates. updateHostText(current, workInProgress, oldText, newText); } else { if (typeof newText !== 'string') { invariant( workInProgress.stateNode !== null, 'We must have new props for new mounts. This error is likely ' + 'caused by a bug in React. Please file an issue.', ); // This can happen when we abort work. } const rootContainerInstance = getRootHostContainer(); const currentHostContext = getHostContext(); let wasHydrated = popHydrationState(workInProgress); if (wasHydrated) { if (prepareToHydrateHostTextInstance(workInProgress)) { markUpdate(workInProgress); } } else { workInProgress.stateNode = createTextInstance( newText, rootContainerInstance, currentHostContext, workInProgress, ); } } break; } case ForwardRef: break; // SuspenseComponent 的处理 case SuspenseComponent: { const nextState = workInProgress.memoizedState; if ((workInProgress.effectTag & DidCapture) !== NoEffect) { // Something suspended. Re-render with the fallback children. workInProgress.expirationTime = renderExpirationTime; // Do not reset the effect list. return workInProgress; } const nextDidTimeout = nextState !== null; const prevDidTimeout = current !== null && current.memoizedState !== null; if (current !== null && !nextDidTimeout && prevDidTimeout) { // We just switched from the fallback to the normal children. Delete // the fallback. // TODO: Would it be better to store the fallback fragment on // the stateNode during the begin phase? const currentFallbackChild: Fiber | null = (current.child: any).sibling; if (currentFallbackChild !== null) { // Deletions go at the beginning of the return fiber's effect list const first = workInProgress.firstEffect; if (first !== null) { workInProgress.firstEffect = currentFallbackChild; currentFallbackChild.nextEffect = first; } else { workInProgress.firstEffect = workInProgress.lastEffect = currentFallbackChild; currentFallbackChild.nextEffect = null; } currentFallbackChild.effectTag = Deletion; } } // The children either timed out after previously being visible, or // were restored after previously being hidden. Schedule an effect // to update their visiblity. if ( // nextDidTimeout !== prevDidTimeout || // Outside concurrent mode, the primary children commit in an // inconsistent state, even if they are hidden. So if they are hidden, // we need to schedule an effect to re-hide them, just in case. ((workInProgress.effectTag & ConcurrentMode) === NoContext && nextDidTimeout) ) { workInProgress.effectTag |= Update; } break; } case Fragment: break; case Mode: break; case Profiler: break; // 后续可以看到每一个基本上都是pop出来一些container,还有context相关的东西 case HostPortal: popHostContainer(workInProgress); updateHostContainer(workInProgress); break; case ContextProvider: // Pop provider fiber popProvider(workInProgress); break; case ContextConsumer: break; case MemoComponent: break; case IncompleteClassComponent: { // Same as class component case. I put it down here so that the tags are // sequential to ensure this switch is compiled to a jump table. const Component = workInProgress.type; if (isLegacyContextProvider(Component)) { popLegacyContext(workInProgress); } break; } default: invariant( false, 'Unknown unit of work tag. This error is likely caused by a bug in ' + 'React. Please file an issue.', ); } return null; }
- 关于
updateHostContainer , 不仅是这个方法,下面的方法appendAllChildren updateHostComponent updateHostText
- 以上4个,会根据环境的不同来执行不同的操作
- 在 ReactDOMHostConfig.js 中
supportsMutation 为true- 这时候
updateHostContainer 就是一个空的方法
- 这时候
- 这里主要关注,HostComponent 以及 HostText,在 completeWork 中的操作
- 在这里,目前只看大致的流程,后续会对细节做具体的分析