在beginWork中生成当前fiber,点击查看详细代码。此处只看常用的IndeterminateComponent、FunctionComponent、ClassComponent、HostRoot、HostComponent、HostText。
function beginWork(current: Fiber | null,workInProgress: Fiber,renderExpirationTime: ExpirationTime,): Fiber | null {const updateExpirationTime = workInProgress.expirationTime;if (current !== null) {const oldProps = current.memoizedProps;const newProps = workInProgress.pendingProps;if (oldProps !== newProps || hasLegacyContextChanged()) {// If props or context changed, mark the fiber as having performed work.// This may be unset if the props are determined to be equal later (memo).didReceiveUpdate = true;} else if (updateExpirationTime < renderExpirationTime) {//更新执行的代码}} else {didReceiveUpdate = false;}// Before entering the begin phase, clear the expiration time.workInProgress.expirationTime = NoWork;switch (workInProgress.tag) {case IndeterminateComponent: {const elementType = workInProgress.elementType;return mountIndeterminateComponent(current,workInProgress,elementType,renderExpirationTime,);}case FunctionComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress.pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);return updateFunctionComponent(current,workInProgress,Component,resolvedProps,renderExpirationTime,);}case ClassComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress.pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);return updateClassComponent(current,workInProgress,Component,resolvedProps,renderExpirationTime,);}case HostRoot:return updateHostRoot(current, workInProgress, renderExpirationTime);case HostComponent:return updateHostComponent(current, workInProgress, renderExpirationTime);case HostText:return updateHostText(current, workInProgress);case IncompleteClassComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress.pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);return mountIncompleteClassComponent(current,workInProgress,Component,resolvedProps,renderExpirationTime,);}}}
beginWork 返回的是workInProgress.child。
HostRoot
function beginWork(current ,workInProgress , renderExpirationTime){case HostRoot:return updateHostRoot(current$$1, workInProgress, renderExpirationTime);}
第一次渲染时,传入的fiber是RootFiber,类型是HostRoot。示例中的App Fiber是updateHostRoot返回的workInProgress.child。初始一次,之后在app中的操作基本上都属于更新。
function updateHostRoot(current, workInProgress, renderExpirationTime) {pushHostRootContext(workInProgress);const updateQueue = workInProgress.updateQueue;const nextProps = workInProgress.pendingProps;const prevState = workInProgress.memoizedState;const prevChildren = prevState !== null ? prevState.element : null;//更新队列,感兴趣可以自己看下,计算得到 memoizedStateprocessUpdateQueue(workInProgress, updateQueue, nextProps, null,renderExpirationTime);const nextState = workInProgress.memoizedState;const nextChildren = nextState.element;// App的ReactElemnetif (nextChildren === prevChildren) {//fiber是复用的,会cloneresetHydrationState();return bailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime,);}const root: FiberRoot = workInProgress.stateNode;if ((current === null || current.child === null) &&root.hydrate &&enterHydrationState(workInProgress)) {//workInProgress.effectTag |= Placement;workInProgress.child = mountChildFibers(workInProgress,null,nextChildren,renderExpirationTime,);} else {//初始执行此处 nextChildren是App ReactElementreconcileChildren(current,workInProgress,nextChildren,renderExpirationTime);resetHydrationState();}return workInProgress.child;}
调用栈:updateHostRoot —> reconcileChildren
HostComponent
function beginWork(current ,workInProgress , renderExpirationTime){case HostComponent:return updateHostComponent(current, workInProgress, renderExpirationTime);}
function updateHostComponent(current, workInProgress, renderExpirationTime) {pushHostContext(workInProgress);if (current === null) {tryToClaimNextHydratableInstance(workInProgress);}const type = workInProgress.type;const nextProps = workInProgress.pendingProps;const prevProps = current !== null ? current.memoizedProps : null;let nextChildren = nextProps.children;const isDirectTextChild = shouldSetTextContent(type, nextProps);if (isDirectTextChild) {//只有一个 文本 子节点,此时文本子节点不再转换为fibernextChildren = null;} else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {workInProgress.effectTag |= ContentReset;//文本重置的 effect}markRef(current, workInProgress);// Check the host config to see if the children are offscreen/hidden.// 在屏幕外的可以不渲染..........if (renderExpirationTime !== Never &&workInProgress.mode & ConcurrentMode &&shouldDeprioritizeSubtree(type, nextProps)) {// Schedule this fiber to re-render at offscreen priority. Then bailout.workInProgress.expirationTime = workInProgress.childExpirationTime = Never;return null;}//通常会走此处reconcileChildren(current,workInProgress,nextChildren,renderExpirationTime,);return workInProgress.child;}
调用栈: updateHostComponent —> reconcileChildren
HostText
function beginWork(current ,workInProgress , renderExpirationTime){case HostText:return updateHostText(current, workInProgress);}
function updateHostText(current, workInProgress) {if (current === null) {tryToClaimNextHydratableInstance(workInProgress);}return null;}
updateHostText中的current是一个fiber,且是beginWork中。文本元素一般没有子元素,直接返回null。在workLoop流程中可以知道返回null,接着会调用completeUnitOfWork —> completeWork ,在completeWork 结束当前的work。
ClassComponent
function beginWork(current ,workInProgress , renderExpirationTime){case ClassComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress.pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);return updateClassComponent(current,workInProgress,Component,resolvedProps,renderExpirationTime);}}
function updateClassComponent(current: Fiber | null,workInProgress: Fiber,Component: any,nextProps,renderExpirationTime: ExpirationTime,) {let hasContext;if (isLegacyContextProvider(Component)) {hasContext = true;pushLegacyContextProvider(workInProgress);} else {hasContext = false;}prepareToReadContext(workInProgress, renderExpirationTime);const instance = workInProgress.stateNode;let shouldUpdate;if (instance === null) {if (current !== null) {current.alternate = null;workInProgress.alternate = null;// Since this is conceptually a new fiber, schedule a Placement effectworkInProgress.effectTag |= Placement;}// In the initial pass we might need to construct the instance.constructClassInstance(workInProgress,Component,nextProps,renderExpirationTime,);mountClassInstance(workInProgress,Component,nextProps,renderExpirationTime,);shouldUpdate = true;} else if (current === null) {// In a resume, we'll already have an instance we can reuse.shouldUpdate = resumeMountClassInstance(workInProgress,Component,nextProps,renderExpirationTime,);} else {shouldUpdate = updateClassInstance(current,workInProgress,Component,nextProps,renderExpirationTime,);}const nextUnitOfWork = finishClassComponent(current,workInProgress,Component,shouldUpdate,hasContext,renderExpirationTime,);return nextUnitOfWork;}
组件的渲染可分为initial、update两阶段。initial阶段执行:constructClassInstance 、mountClassInstance;update阶段执行updateClassInstance。阶段执行完后执行 finishClassComponent,组件是否更新还依赖于shouldUpdate参数。
initial-constructClassInstance
执行class 的construct 方法,创建class 实例
function constructClassInstance(workInProgress: Fiber,ctor: any,props: any,renderExpirationTime: ExpirationTime,): any {let isLegacyContextConsumer = false;let unmaskedContext = emptyContextObject;let context = null;const contextType = ctor.contextType;if (typeof contextType === 'object' && contextType !== null) {context = readContext((contextType: any));} else {unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);const contextTypes = ctor.contextTypes;isLegacyContextConsumer =contextTypes !== null && contextTypes !== undefined;context = isLegacyContextConsumer? getMaskedContext(workInProgress, unmaskedContext): emptyContextObject;}const instance = new ctor(props, context);//执行classComponent的实例const state = (workInProgress.memoizedState =instance.state !== null && instance.state !== undefined? instance.state: null);//workInProgress.memoizedState是新的stateadoptClassInstance(workInProgress, instance);if (isLegacyContextConsumer) {cacheContext(workInProgress, unmaskedContext, context);}return instance;}
initial-mountClassInstance
// Invokes the mount life-cycles on a previously never rendered instance.function mountClassInstance(workInProgress: Fiber,ctor: any,newProps: any,renderExpirationTime: ExpirationTime,): void {const instance = workInProgress.stateNode;instance.props = newProps;instance.state = workInProgress.memoizedState;instance.refs = emptyRefsObject;const contextType = ctor.contextType;if (typeof contextType === 'object' && contextType !== null) {instance.context = readContext(contextType);} else {const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);instance.context = getMaskedContext(workInProgress, unmaskedContext);}let updateQueue = workInProgress.updateQueue;if (updateQueue !== null) {//有 更新队列processUpdateQueue(workInProgress,updateQueue,newProps,instance,renderExpirationTime);instance.state = workInProgress.memoizedState;}const getDerivedStateFromProps = ctor.getDerivedStateFromProps;if (typeof getDerivedStateFromProps === 'function') {applyDerivedStateFromProps(workInProgress,ctor,getDerivedStateFromProps,newProps);instance.state = workInProgress.memoizedState;}// In order to support react-lifecycles-compat polyfilled components,// Unsafe lifecycles should not be invoked for components using the new APIs.if (typeof ctor.getDerivedStateFromProps !== 'function' &&typeof instance.getSnapshotBeforeUpdate !== 'function' &&(typeof instance.UNSAFE_componentWillMount === 'function' ||typeof instance.componentWillMount === 'function')) {callComponentWillMount(workInProgress, instance);// If we had additional state updates during this life-cycle, let's// process them now.updateQueue = workInProgress.updateQueue;if (updateQueue !== null) {processUpdateQueue(workInProgress,updateQueue,newProps,instance,renderExpirationTime);instance.state = workInProgress.memoizedState;}}if (typeof instance.componentDidMount === 'function') {workInProgress.effectTag |= Update;}}
执行mountClassInstance 中更新队列、执行getDerivedStateFromProps、componentWillMount生命周期,同时为componentDidMount打effectTag。initial 执行mountClassInstance ,更新阶段则主要执行updateClassInstance。
update-updateClassInstance
updateClassInstance时先执行getDerivedStateFromProps 、shouldUpdate= checkShouldComponentUpdate();
/ Invokes the update life-cycles and returns false if it shouldn't rerender.function updateClassInstance(current: Fiber,workInProgress: Fiber,ctor: any,newProps: any,renderExpirationTime: ExpirationTime,): boolean {const instance = workInProgress.stateNode;const oldProps = workInProgress.memoizedProps;instance.props = orkInProgress.type === workInProgress.elementType ? oldProps: resolveDefaultProps(workInProgress.type, oldProps);const oldContext = instance.context;const contextType = ctor.contextType;let nextContext;if (typeof contextType === 'object' && contextType !== null) {nextContext = readContext(contextType);} else {const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);}const getDerivedStateFromProps = ctor.getDerivedStateFromProps;const hasNewLifecycles =typeof getDerivedStateFromProps === 'function' ||typeof instance.getSnapshotBeforeUpdate === 'function';// In order to support react-lifecycles-compat polyfilled components,// Unsafe lifecycles should not be invoked for components using the new APIs.if (!hasNewLifecycles &&(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||typeof instance.componentWillReceiveProps === 'function')) {if (oldProps !== newProps || oldContext !== nextContext) {callComponentWillReceiveProps( workInProgress, instance, newProps,nextContext ;}}resetHasForceUpdateBeforeProcessing();const oldState = workInProgress.memoizedState;let newState = (instance.state = oldState);let updateQueue = workInProgress.updateQueue;if (updateQueue !== null) {processUpdateQueue( workInProgress,updateQueue,newProps,instance,renderExpirationTime);newState = workInProgress.memoizedState;}if (oldProps === newProps && oldState === newState && !hasContextChanged() &&!checkHasForceUpdateAfterProcessing()) {// If an update was already in progress, we should schedule an Update// effect even though we're bailing out, so that cWU/cDU are called.if (typeof instance.componentDidUpdate === 'function') {if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {workInProgress.effectTag |= Update;}}if (typeof instance.getSnapshotBeforeUpdate === 'function') {if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {workInProgress.effectTag |= Snapshot;}}return false; //返回false,不向下执行}if (typeof getDerivedStateFromProps === 'function') {applyDerivedStateFromProps(workInProgress,ctor,getDerivedStateFromProps,newProps,);newState = workInProgress.memoizedState;}const shouldUpdate =checkHasForceUpdateAfterProcessing() ||checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState,newState, nextContext);if (shouldUpdate) {// In order to support react-lifecycles-compat polyfilled components,// Unsafe lifecycles should not be invoked for components using the new APIs.if (!hasNewLifecycles &&(typeof instance.UNSAFE_componentWillUpdate === 'function' ||typeof instance.componentWillUpdate === 'function')) {startPhaseTimer(workInProgress, 'componentWillUpdate');if (typeof instance.componentWillUpdate === 'function') {instance.componentWillUpdate(newProps, newState, nextContext);}if (typeof instance.UNSAFE_componentWillUpdate === 'function') {instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);}stopPhaseTimer();}if (typeof instance.componentDidUpdate === 'function') {workInProgress.effectTag |= Update;}if (typeof instance.getSnapshotBeforeUpdate === 'function') {workInProgress.effectTag |= Snapshot;}} else {// If an update was already in progress, we should schedule an Update// effect even though we're bailing out, so that cWU/cDU are called.if (typeof instance.componentDidUpdate === 'function') {if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {workInProgress.effectTag |= Update;}}if (typeof instance.getSnapshotBeforeUpdate === 'function') {if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {workInProgress.effectTag |= Snapshot;}}// If shouldComponentUpdate returned false, we should still update the// memoized props/state to indicate that this work can be reused.workInProgress.memoizedProps = newProps;workInProgress.memoizedState = newState;}// Update the existing instance's state, props, and context pointers even// if shouldComponentUpdate returns false.instance.props = newProps;instance.state = newState;instance.context = nextContext;return shouldUpdate;}
componentDidUpdate和getSnapshotBeforeUpdate是标记,componentWillUpdate是调用。返回的值是boolean值。
finishClassComponent
function finishClassComponent(current: Fiber | null,workInProgress: Fiber,Component: any,shouldUpdate: boolean,hasContext: boolean,renderExpirationTime: ExpirationTime,) {// Refs should update even if shouldComponentUpdate returns falsemarkRef(current, workInProgress);const didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;if (!shouldUpdate && !didCaptureError) {// Context providers should defer to sCU for renderingif (hasContext) {invalidateContextProvider(workInProgress, Component, false);}return bailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime);}const instance = workInProgress.stateNode;// RerenderReactCurrentOwner.current = workInProgress;let nextChildren;if (didCaptureError &&typeof Component.getDerivedStateFromError !== 'function') {// If we captured an error, but getDerivedStateFrom catch is not defined,// unmount all the children. componentDidCatch will schedule an update to// re-render a fallback. This is temporary until we migrate everyone to// the new API.// TODO: Warn in a future release.nextChildren = null;if (enableProfilerTimer) {stopProfilerTimerIfRunning(workInProgress);}} else {nextChildren = instance.render();}// React DevTools reads this flag.workInProgress.effectTag |= PerformedWork;if (current !== null && didCaptureError) {// If we're recovering from an error, reconcile without reusing any of// the existing children. Conceptually, the normal children and the children// that are shown on error are two different sets, so we shouldn't reuse// normal children even if their identities match.forceUnmountCurrentAndReconcile(current, workInProgress,nextChildren,renderExpirationTime);} else {reconcileChildren(current,workInProgress,nextChildren,renderExpirationTime);}workInProgress.memoizedState = instance.state;if (hasContext) {invalidateContextProvider(workInProgress, Component, true);}return workInProgress.child;}
finishClassComponent中的 shouldUpdate = false,相当于组件 shouldComponentUpdate 返回false,组件不做更新。
调用栈: updateClassComponent—> reconcileChildren;
FunctionComponent
function beginWork(current ,workInProgress , renderExpirationTime){case FunctionComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress.pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);return updateFunctionComponent(current,workInProgress,Component,resolvedProps,renderExpirationTime);}}
function updateFunctionComponent(current,workInProgress,Component,nextProps: any,renderExpirationTime) {const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);const context = getMaskedContext(workInProgress, unmaskedContext);let nextChildren;prepareToReadContext(workInProgress, renderExpirationTime);nextChildren = renderWithHooks(current,workInProgress,Component,nextProps,context, renderExpirationTime);if (current !== null && !didReceiveUpdate) {//beginWork的值bailoutHooks(current, workInProgress, renderExpirationTime);return bailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime);}// React DevTools reads this flag.workInProgress.effectTag |= PerformedWork;reconcileChildren(current,workInProgress,nextChildren,renderExpirationTime);return workInProgress.child;}
调用栈: updateFunctionComponent —> reconcileChildren
IndeterminateComponent
function mountIndeterminateComponent(_current,workInProgress,Component,renderExpirationTime,) {if (_current !== null) {// An indeterminate component only mounts if it suspended inside a non-// concurrent tree, in an inconsistent state. We want to treat it like// a new mount, even though an empty version of it already committed.// Disconnect the alternate pointers._current.alternate = null;workInProgress.alternate = null;// Since this is conceptually a new fiber, schedule a Placement effectworkInProgress.effectTag |= Placement;}const props = workInProgress.pendingProps;const unmaskedContext = getUnmaskedContext(workInProgress, Component, false);const context = getMaskedContext(workInProgress, unmaskedContext);prepareToReadContext(workInProgress, renderExpirationTime);let value = renderWithHooks( null,workInProgress,Component,props,context,renderExpirationTime);// React DevTools reads this flag.workInProgress.effectTag |= PerformedWork;if (typeof value === 'object' && value !== null &&typeof value.render === 'function' &&value.$$typeof === undefined) {// Proceed under the assumption that this is a class instanceworkInProgress.tag = ClassComponent;// Throw out any hooks that were used.resetHooks();// Push context providers early to prevent context stack mismatches.// During mounting we don't know the child context yet as the instance doesn't exist.// We will invalidate the child context in finishClassComponent() right after rendering.let hasContext = false;if (isLegacyContextProvider(Component)) {hasContext = true;pushLegacyContextProvider(workInProgress);} else {hasContext = false;}workInProgress.memoizedState =value.state !== null && value.state !== undefined ? value.state : null;const getDerivedStateFromProps = Component.getDerivedStateFromProps;if (typeof getDerivedStateFromProps === 'function') {applyDerivedStateFromProps(workInProgress,Component,getDerivedStateFromProps, props);}adoptClassInstance(workInProgress, value);mountClassInstance(workInProgress, Component, props, renderExpirationTime);return finishClassComponent(null,workInProgress,Component,true,hasContext,renderExpirationTime ;} else {// Proceed under the assumption that this is a function componentworkInProgress.tag = FunctionComponent;reconcileChildren(null, workInProgress, value, renderExpirationTime);return workInProgress.child;}}
在createFiberFromTypeAndProps 创建function Component时默认类型为:IndeterminateComponent。在mountIndeterminateComponent中确认是workInProgress.tag并调用相应的方法。
调用栈: mountIndeterminateComponent —> reconcileChildren;
想法:这种方式可以将组件变得更加灵活?想象下,组件的属性都是方法组合的。
小结
在beginWork 做了prop和state判断,判断是否更新;updateClassComponent中shouldUpdate判断是否更新;
多数beginWork最后调用的都是 reconcileChildren,同时返回的是workInProgress.child。由workLoop流程可知道work在 completeUnitOfWork 结束。
