学习资料:https://react.iamkasong.com/process/reconciler.html#%E9%80%92-%E9%98%B6%E6%AE%B5
render阶段过程:采用深度优先遍历的方式,依次执行beginWork和completeWork
第一次进入beginWork,存在current,tag为3
3表示是根节点
下一次执行,进入function App的beginWork,对于双缓存机制,再一次进入current为null,
存在workInProgress
测试demo
再执行一次,下一个进入beginWork的是div
再执行一次,下一个进入beginWork的是header
再执行一次,下一个进入beginWork的是img
由于img没有子节点,执行completeWork
img的completeWork执行完后,会执行img的兄弟节点P的BeginWork
此时p有3个子节点,Edit
、<code>src/App.js</code>
、and save to reload
,下一次执行,进入p的子节点Edit
由于Edit没有子节点,会进入Edit的completeWork
Edit的completeWork执行完之后,会进入Edit的兄弟节点的beginWork
在执行一次,进入了code的completeWork,因为React对只有一个文本节点的子节点做了优化,这个子节点不会产生自己的Fiber节点。因此下一次执行了code的completeWork。
接下来,code寻找自己的兄弟节点文本节点and save to reload的beginWork,再继续执行文本节点的completeWork。
下一次执行,由于文本节点没有兄弟节点,会执行p的completeWork。
下一次执行,a节点的beginWork。
下一次执行,由于a节点的子节点是文本节点,执行completeWork。
下一次执行,由于a节点没有兄弟节点,执行header的completeWork。
下一次执行,由于header节点没有兄弟节点,执行#app div的completeWork。
下一次执行,由于#app div节点没有兄弟节点,执行方法App的completeWork。
再一次执行,App的根节点,即tag为3的根节点的completeWork。
在接下来,rander阶段完成,进入commit阶段,渲染页面。
BeginWork的作用 —传入当前Fiber节点,创建子Fiber节点,并将者两个Fiber节点连接起来。当遍历到叶子节点(即没有子节点的组件)时,就会进入”归”阶段。
function beginWork(current$$1, workInProgress, renderExpirationTime) {
var updateExpirationTime = workInProgress.expirationTime;
...
// 根据不同的tag,进入不同的case
workInProgress.expirationTime = NoWork;
switch (workInProgress.tag) {
case IndeterminateComponent:
...
case LazyComponent:
...
case FunctionComponent:
...
case ClassComponent:
...
case HostRoot:
return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
case HostComponent:
return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
case HostText:
return updateHostText(current$$1, workInProgress);
case SuspenseComponent:
return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
case HostPortal:
return updatePortalComponent(current$$1, workInProgress, renderExpirationTime);
case ForwardRef:
...
}
}
updateHostComponent
以div举例,div是HostComponent,进入updateHostComponent
function updateHostComponent(current$$1, workInProgress, renderExpirationTime) {
pushHostContext(workInProgress);
if (current$$1 === null) {
tryToClaimNextHydratableInstance(workInProgress);
}
//先赋值
var type = workInProgress.type;
var nextProps = workInProgress.pendingProps;
var prevProps = current$$1 !== null ? current$$1.memoizedProps : null;
var nextChildren = nextProps.children;
//检查当前的fiber节点,是否只有一个唯一的文本子节点,比如a标签,
//如果是唯一的文本子节点,react不会创建fiber节点(优化点)
var isDirectTextChild = shouldSetTextContent(type, nextProps);
if (isDirectTextChild) {
nextChildren = null;
} else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
workInProgress.effectTag |= ContentReset;
}
markRef(current$$1, 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方法
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
return workInProgress.child;
}
reconcileChildren
再执行reconcileChildren之前,当前的workInProgress.child是null

function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
if (current$$1 === null) {
/*如果这是一个尚未渲染的全新组件,我们
不会通过应用最小的副作用来更新其子集。相反
在渲染之前,我们会将它们全部添加到子级中。这意味着
我们可以通过不跟踪副作用来优化这种对账传递。*/
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
} else {
/* 如果当前子项与正在进行的工作相同,则意味着
我们还没有开始对这些孩子进行任何研究。因此,我们使用
克隆算法以创建所有当前子级的副本。
如果我们已经有任何进展,那么在这一点上是无效的,所以
让我们把它扔掉。*/
workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, nextChildren, renderExpirationTime);
}
}
mountChildFibers与mountChildFiber都是ChildReconciler方法根据不同的Boolean值处理的。
export const reconcileChildFibers = ChildReconciler(true);
export const mountChildFibers = ChildReconciler(false);
查看Placement的值,
ReactFiberFlags.js保存了所有的副作用,render阶段不会进行具体dom操作,具体dom操作是在commit阶段执行的,render阶段为需要执行操作的fiber节点打上标记。
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
export type Flags = number;
// Don't change these two values. They're used by React Dev Tools.
export const NoFlags = /* */ 0b00000000000000000000000000;
export const PerformedWork = /* */ 0b00000000000000000000000001;
// You can change the rest (and add more).
export const Placement = /* */ 0b00000000000000000000000010;
export const Update = /* */ 0b00000000000000000000000100;
export const Deletion = /* */ 0b00000000000000000000001000;
export const ChildDeletion = /* */ 0b00000000000000000000010000;
export const ContentReset = /* */ 0b00000000000000000000100000;
export const Callback = /* */ 0b00000000000000000001000000;
export const DidCapture = /* */ 0b00000000000000000010000000;
export const ForceClientRender = /* */ 0b00000000000000000100000000;
export const Ref = /* */ 0b00000000000000001000000000;
export const Snapshot = /* */ 0b00000000000000010000000000;
export const Passive = /* */ 0b00000000000000100000000000;
export const Hydrating = /* */ 0b00000000000001000000000000;
export const Visibility = /* */ 0b00000000000010000000000000;
export const StoreConsistency = /* */ 0b00000000000100000000000000;
export const LifecycleEffectMask =
Passive | Update | Callback | Ref | Snapshot | StoreConsistency;
// Union of all commit flags (flags with the lifetime of a particular commit)
export const HostEffectMask = /* */ 0b00000000000111111111111111;
// These are not really side effects, but we still reuse this field.
export const Incomplete = /* */ 0b00000000001000000000000000;
export const ShouldCapture = /* */ 0b00000000010000000000000000;
export const ForceUpdateForLegacySuspense = /* */ 0b00000000100000000000000000;
export const DidPropagateContext = /* */ 0b00000001000000000000000000;
export const NeedsPropagation = /* */ 0b00000010000000000000000000;
export const Forked = /* */ 0b00000100000000000000000000;
// Static tags describe aspects of a fiber that are not specific to a render,
// e.g. a fiber uses a passive effect (even if there are no updates on this particular render).
// This enables us to defer more work in the unmount case,
// since we can defer traversing the tree during layout to look for Passive effects,
// and instead rely on the static flag as a signal that there may be cleanup work.
export const RefStatic = /* */ 0b00001000000000000000000000;
export const LayoutStatic = /* */ 0b00010000000000000000000000;
export const PassiveStatic = /* */ 0b00100000000000000000000000;
// These flags allow us to traverse to fibers that have effects on mount
// without traversing the entire tree after every commit for
// double invoking
export const MountLayoutDev = /* */ 0b01000000000000000000000000;
export const MountPassiveDev = /* */ 0b10000000000000000000000000;
// Groups of flags that are used in the commit phase to skip over trees that
// don't contain effects, by checking subtreeFlags.
export const BeforeMutationMask =
// TODO: Remove Update flag from before mutation phase by re-landing Visibility
// flag logic (see #20043)
Update |
Snapshot |
(enableCreateEventHandleAPI
? // createEventHandle needs to visit deleted and hidden trees to
// fire beforeblur
// TODO: Only need to visit Deletions during BeforeMutation phase if an
// element is focused.
ChildDeletion | Visibility
: 0);
export const MutationMask =
Placement |
Update |
ChildDeletion |
ContentReset |
Ref |
Hydrating |
Visibility;
export const LayoutMask = Update | Callback | Ref | Visibility;
// TODO: Split into PassiveMountMask and PassiveUnmountMask
export const PassiveMask = Passive | ChildDeletion;
// Union of tags that don't get reset on clones.
// This allows certain concepts to persist without recalculating them,
// e.g. whether a subtree contains passive effects or portals.
export const StaticMask = LayoutStatic | PassiveStatic | RefStatic;
reconcileChildFibers
进入reconcileChildFibers,
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
var isUnkeyedTopLevelFragment =
typeof newChild === "object" && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null
if (isUnkeyedTopLevelFragment) {
newChild = newChild.props.children
}
// 判断child类型,对不同类型,进入不同处理逻辑
var isObject = typeof newChild === "object" && newChild !== null
if (isObject) {
switch (newChild.$$typeof) {
//如果值是REACT_ELEMENT_TYPE,被当作一个单一的react_elment来处理
case REACT_ELEMENT_TYPE:
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime))
case REACT_PORTAL_TYPE:
return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime))
}
}
//如果当前的newChild是string或者number类型,会当作一个文本节点处理
if (typeof newChild === "string" || typeof newChild === "number") {
return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, "" + newChild, expirationTime))
}
//如果当前的newChild是数组,会当作一个数组处理。数组情况,例如header中有img,p,a三个子节点,即会被当作数组处理。
if (isArray(newChild)) {
return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime)
}
...
}
reconcileSingleElement
进入reconcileSingleElement,

function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) {
var key = element.key
var child = currentFirstChild
while (child !== null) {
// TODO: If key === null and child.key === null, then this only applies to
// the first item in the list.
if (child.key === key) {
if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type) {
deleteRemainingChildren(returnFiber, child.sibling)
var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime)
existing.ref = coerceRef(returnFiber, child, element)
existing.return = returnFiber
{
existing._debugSource = element._source
existing._debugOwner = element._owner
}
return existing
} else {
deleteRemainingChildren(returnFiber, child)
break
}
} else {
deleteChild(returnFiber, child)
}
child = child.sibling
}
if (element.type === REACT_FRAGMENT_TYPE) {
var created = createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime, element.key)
created.return = returnFiber
return created
} else {
//最终进入这里
var _created4 = createFiberFromElement(element, returnFiber.mode, expirationTime)
_created4.ref = coerceRef(returnFiber, currentFirstChild, element)
_created4.return = returnFiber
return _created4
}
}
createFiberFromElement
进入createFiberFromElement
export function createFiberFromElement(
element: ReactElement,
mode: TypeOfMode,
lanes: Lanes,
): Fiber {
let owner = null;
...
const type = element.type;
const key = element.key;
const pendingProps = element.props;
const fiber = createFiberFromTypeAndProps(
type,
key,
pendingProps,
owner,
mode,
lanes,
);
...
return fiber;
}
createFiberFromTypeAndProps
createFiberFromTypeAndProps
判断type类型,当前type类型为string,于是fiberTag = HostComponent;
export function createFiberFromTypeAndProps(
type: any, // React$ElementType
key: null | string,
pendingProps: any,
owner: null | Fiber,
mode: TypeOfMode,
lanes: Lanes,
): Fiber {
let fiberTag = IndeterminateComponent;
//判断type类型
let resolvedType = type;
if (typeof type === 'function') {
if (shouldConstruct(type)) {
fiberTag = ClassComponent;
}
} else if (typeof type === 'string') {
fiberTag = HostComponent;
} else {
...
}
const fiber = createFiber(fiberTag, pendingProps, key, mode);
fiber.elementType = type;
fiber.type = resolvedType;
fiber.lanes = lanes;
return fiber;
}
createFiber
进入createFiber,创建对应的fiber节点
const createFiber = function(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
): Fiber {
//创建Fiber节点
return new FiberNode(tag, pendingProps, key, mode);
};
FiberNode
进入FiberNode,有很多的属性
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// Fiber
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// Effects
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
if (enableProfilerTimer) {
this.actualDuration = Number.NaN;
this.actualStartTime = Number.NaN;
this.selfBaseDuration = Number.NaN;
this.treeBaseDuration = Number.NaN;
this.actualDuration = 0;
this.actualStartTime = -1;
this.selfBaseDuration = 0;
this.treeBaseDuration = 0;
}
...
}
总结

当某一个Fiber进入beginWork时,他最终的目的是为了创建当前Fiber节点的第一个子Fiber节点
首先,进入UpdateHostComponent,判断当前Fiber节点的类型,进入不同update的逻辑
在reconcileChildren逻辑中,判断workInProgressFiber是否存在对应的currentFiber来决定是否标记effectTag
reconcileChildFibers:根据child节点的类型执行创建操作
最终进入createFiber,创建一个Fiber树。
当进入reconcileChildrenArray,该函数创建当前Fiber节点的子节点

此时,returnFiber指的是header,即创建img节点,而不是img,p,a子Fiber节点
