从createElement开始
createElment(type, config, …children):生成虚拟DOM对象。babel转换react时调用该方法将jsx语法转换成js语法。
createElment主要完成以下功能:
- 处理config中的特殊属性:ref、key、__self、__source。
- 将config中的非特殊属性保存到props中。
- 将第3个参数开始以后的参数记录到一个数组中,并保存到props.children上。
- 为props中的属性赋默认值。
- 创建ReactElement对象(即虚拟DOM对象)并返回。
文件位置:react\packages\react\src\ReactElement.js
/**
* Create and return a new ReactElement of the given type.
* See https://reactjs.org/docs/react-api.html#createelement
* 1.从config中提取特殊属性
* 2.将config中的非特殊属性存放到props中
* 3.将子元素对象存放到props.children中
* 4.为props的属性设置默认值
* 5.创建并返回reactElement对象
*/
export function createElement(type, config, children) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
// 从config中提取特殊属性,并将其他属性存放到props中
if (config != null) {
// 判断ref可用
if (hasValidRef(config)) {
ref = config.ref;
if (__DEV__) {
warnIfStringRefCannotBeAutoConverted(config);
}
}
if (hasValidKey(config)) {
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
// 排除特殊属性 key ref __self __source 将其他属性保存到props中
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
// 从第三个参数开始为子节点
// 如果只有一个子节点,则props.children为一个对象
// 如果有多个子节点,props.children为一个数组
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
// Resolve default props
// 为props中的属性设置默认值
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
// 开发环境下如果通过props去获取特殊属性的值,提示警告
if (__DEV__) {
if (key || ref) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
渲染虚拟DOM
虚拟DOM的渲染分为几个阶段:
1 创建FiberRoot和RootFiber对象。
FiberRoot是react的根fiber对象,每个react应用只有一个FiberRoot对象。
RootFiber是调用render方法传入的container容器对应的fiber对象,每次调用render方法会生成一个。
FiberRoot和RootFiber相互关联。FiberRoot.current = RootFiber,RootFiber.stateNode = FiberRoot。
/**
* 创建fiberRoot对象
* @param {*} containerInfo 容器信息
* @param {*} tag
* @param {*} hydrate 是否是服务端渲染
* @param {*} hydrationCallbacks
* @returns
*/
export function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
// 创建FiberRoot对象
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
// false
if (enableSuspenseCallback) {
root.hydrationCallbacks = hydrationCallbacks;
}
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
// 创建根rootFiber对象
const uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
// 初始化fiber的updateQueue
initializeUpdateQueue(uninitializedFiber);
return root;
}
2 初始化任务队列,并向任务队列中添加任务
执行的任务都是从rootFiber的任务队列updateQueue中获取的。初始化任务队列则是构建一个基本结构的任务对象,并将其赋值给fiberRoot.updateQueue。
/**
* 初始化fiber对象的 updateQueue 。
* @param {*} fiber
*/
export function initializeUpdateQueue<State>(fiber: Fiber): void {
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState, // 上一次更新之后state,作为这一次任务的基础的state
baseQueue: null,
shared: {
pending: null,
},
effects: null,
};
fiber.updateQueue = queue;
}
创建一个任务对象,并指定任务内容。
export function createUpdate(
expirationTime: ExpirationTime,
suspenseConfig: null | SuspenseConfig,
): Update<*> {
let update: Update<*> = {
expirationTime,
suspenseConfig,
tag: UpdateState,
payload: null,
callback: null,
next: (null: any),
};
update.next = update;
if (__DEV__) {
update.priority = getCurrentPriorityLevel();
}
return update;
}
// 获取修改任务,即创建一个任务对象
const update = createUpdate(expirationTime, suspenseConfig);
// Caution: React DevTools currently depends on this property
// being called "element".
// 设置任务目标对象,任务要操作谁
update.payload = {element};
callback = callback === undefined ? null : callback;
if (callback !== null) {
if (__DEV__) {
if (typeof callback !== 'function') {
console.error(
'render(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
}
}
update.callback = callback;
}
将任务添加到任务队列中,这里的任务队列使用链表结构来存储。
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return;
}
const sharedQueue = updateQueue.shared;
const pending = sharedQueue.pending;
// 构建任务的链表结构
if (pending === null) {
// This is the first update. Create a circular list.
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update;
}
3 构建workInProgress树
初始化workInProgress对象。
// current --> rootFiber
workInProgress = createFiber(
current.tag,
pendingProps,
current.key,
current.mode,
);
workInProgress.elementType = current.elementType;
workInProgress.type = current.type;
workInProgress.stateNode = current.stateNode;
开始循环构建workInProgress fiber 树。
// The work loop is an extremely hot path. Tell Closure not to inline it.
/** @noinline */
function workLoopSync() {
// Already timed out, so perform work without checking if we need to yield.
while (workInProgress !== null) {
workInProgress = performUnitOfWork(workInProgress);
}
}
先从上到下构建第一子节点,然后从下到上构建其他子节点。
function performUnitOfWork(unitOfWork: Fiber): Fiber | null {
// 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.
const current = unitOfWork.alternate;
startWorkTimer(unitOfWork);
setCurrentDebugFiberInDEV(unitOfWork);
let next;
if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
startProfilerTimer(unitOfWork);
next = beginWork(current, unitOfWork, renderExpirationTime);
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
} else {
next = beginWork(current, unitOfWork, renderExpirationTime);
}
resetCurrentDebugFiberInDEV();
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// If this doesn't spawn new work, complete the current work.
next = completeUnitOfWork(unitOfWork);
}
ReactCurrentOwner.current = null;
return next;
}
beforeWork:从rootFiber开始构建子节点对应的fiber对象。最终调用reconcileChildFibers方法构建子节点。
function reconcileChildFibers(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
expirationTime: ExpirationTime,
): Fiber | null {
const isUnkeyedTopLevelFragment =
typeof newChild === 'object' &&
newChild !== null &&
newChild.type === REACT_FRAGMENT_TYPE &&
newChild.key === null;
if (isUnkeyedTopLevelFragment) {
newChild = newChild.props.children;
}
// Handle object types
const isObject = typeof newChild === 'object' && newChild !== null;
if (isObject) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
return placeSingleChild(
reconcileSingleElement(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
),
);
case REACT_PORTAL_TYPE:
return placeSingleChild(
reconcileSinglePortal(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
),
);
}
}
if (typeof newChild === 'string' || typeof newChild === 'number') {
return placeSingleChild(
reconcileSingleTextNode(
returnFiber,
currentFirstChild,
'' + newChild,
expirationTime,
),
);
}
if (isArray(newChild)) {
return reconcileChildrenArray(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
);
}
if (getIteratorFn(newChild)) {
return reconcileChildrenIterator(
returnFiber,
currentFirstChild,
newChild,
expirationTime,
);
}
if (isObject) {
throwOnInvalidObjectType(returnFiber, newChild);
}
// Remaining cases are all treated as empty.
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
如果只有一个子节点,调用reconcileSingleElement来创建子元素对应的fiber。
function reconcileSingleElement(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
element: ReactElement,
expirationTime: ExpirationTime,
): Fiber {
const key = element.key;
let child = currentFirstChild;
// 初次渲染核心内容
const created = createFiberFromElement(
element,
returnFiber.mode,
expirationTime,
);
created.ref = coerceRef(returnFiber, currentFirstChild, element);
created.return = returnFiber;
return created;
}
如果有多个子节点,则调用reconcileChildrenArray生成子节点对应的fiber
function reconcileChildrenArray(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChildren: Array<*>,
expirationTime: ExpirationTime,
): Fiber | null {
let resultingFirstChild: Fiber | null = null;
let previousNewFiber: Fiber | null = null;
let oldFiber = currentFirstChild;
let lastPlacedIndex = 0;
let newIdx = 0;
let nextOldFiber = null;
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
// 创建子节点fiber
const newFiber = updateSlot(
returnFiber,
oldFiber,
newChildren[newIdx],
expirationTime,
);
if (shouldTrackSideEffects) {
if (oldFiber && newFiber.alternate === null) {
// We matched the slot, but we didn't reuse the existing fiber, so we
// need to delete the existing child.
deleteChild(returnFiber, oldFiber);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else { // 设置兄弟节点
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
oldFiber = nextOldFiber;
}
return resultingFirstChild;
}
beginWork完成了子节点及第一个子节点的子节点的fiber构建。fiber.child指向子节点的fiber对象,fiber.sibling则指向兄弟节点的fiber。
completeUnitOfWork:自底向上构建其他节点的子节点fiber。completeUnitOfWork过程中,会遍历到所有节点,完成以下操作:
- 创建fiber对象对应的真实DOM,并保存到fiber.stateNode上。
- 收集待进行DOM操作的fiber对象,保存在effect链表结构中。
- 返回兄弟节点fiber,如果没有则回退到父节点fiber,直到父节点fiber为null,即回退到了contianer容器的fiber。
/**
*
* 从下至上移动到该节点的兄弟节点, 如果一直往上没有兄弟节点就返回父节点, 最终会到达 root 节点
* 1. 创建其他节点的 Fiber 对象
* 2. 创建每一个节点的真实 DOM 对象并将其添加到 stateNode 属性中
* 3. 构建 effect 链表结构
*/
function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {
// 为 workInProgress 全局变量重新赋值
workInProgress = unitOfWork;
do {
// 获取备份节点
// 初始化渲染 非根 Fiber 对象没有备份节点 所以 current 为 null
const current = workInProgress.alternate;
// 父级 Fiber 对象, 非根 Fiber 对象都有父级
const returnFiber = workInProgress.return;
// 判断传入的 Fiber 对象是否构建完成, 任务调度相关
// & 是表示位的与运算, 把左右两边的数字转化为二进制
// 然后每一位分别进行比较, 如果相等就为1, 不相等即为0
// 此处应用"位与"运算符的目的是"清零"
// true
if ((workInProgress.effectTag & Incomplete) === NoEffect) {
let next;
// 如果不能使用分析器的 timer, 直接执行 completeWork
// enableProfilerTimer => true
// 但此处无论条件是否成立都会执行 completeWork
if (
!enableProfilerTimer ||
(workInProgress.mode & ProfileMode) === NoMode
) {
// 重点代码(二)
// 创建节点真实 DOM 对象并将其添加到 stateNode 属性中
next = completeWork(current, workInProgress, renderExpirationTime);
} else {
// 创建节点真实 DOM 对象并将其添加到 stateNode 属性中
next = completeWork(current, workInProgress, renderExpirationTime);
}
// 重点代码(一)
// 如果子级存在
if (next !== null) {
// 返回子级 一直返回到 workLoopSync
// 再重新执行 performUnitOfWork 构建子级 Fiber 节点对象
return next;
}
// 构建 effect 链表结构
// 如果不是根 Fiber 就是 true 否则就是 false
// 将子树和此 Fiber 的所有 effect 附加到父级的 effect 列表中
if (
// 如果父 Fiber 存在 并且
returnFiber !== null &&
// 父 Fiber 对象中的 effectTag 为 0
(returnFiber.effectTag & Incomplete) === NoEffect
) {
// 将子树和此 Fiber 的所有副作用附加到父级的 effect 列表上
// 以下两个判断的作用是搜集子 Fiber的 effect 到父 Fiber
if (returnFiber.firstEffect === null) {
// first
returnFiber.firstEffect = workInProgress.firstEffect;
}
if (workInProgress.lastEffect !== null) {
if (returnFiber.lastEffect !== null) {
// next
returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
}
// last
returnFiber.lastEffect = workInProgress.lastEffect;
}
// 获取副作用标记
// 初始渲染时除[根组件]以外的 Fiber, effectTag 值都为 0, 即不需要执行任何真实DOM操作
// 根组件的 effectTag 值为 3, 即需要将此节点对应的真实DOM对象添加到页面中
const effectTag = workInProgress.effectTag;
// 创建 effect 列表时跳过 NoWork(0) 和 PerformedWork(1) 标记
// PerformedWork 由 React DevTools 读取, 不提交
// 初始渲染时 只有遍历到了根组件 判断条件才能成立, 将 effect 链表添加到 rootFiber
// 初始渲染 FiberRoot 对象中的 firstEffect 和 lastEffect 都是 App 组件
// 因为当所有节点在内存中构建完成后, 只需要一次将所有 DOM 添加到页面中
if (effectTag > PerformedWork) {
// false
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = workInProgress;
} else {
// 为 fiberRoot 添加 firstEffect
returnFiber.firstEffect = workInProgress;
}
// 为 fiberRoot 添加 lastEffect
returnFiber.lastEffect = workInProgress;
}
}
} else {
// 忽略了初始渲染不执行的代码
}
// 获取下一个同级 Fiber 对象
const siblingFiber = workInProgress.sibling;
// 如果下一个同级 Fiber 对象存在
if (siblingFiber !== null) {
// 返回下一个同级 Fiber 对象
return siblingFiber;
}
// 否则退回父级
workInProgress = returnFiber;
} while (workInProgress !== null);
// 当执行到这里的时候, 说明遍历到了 root 节点, 已完成遍历
// 更新 workInProgressRootExitStatus 的状态为 已完成
if (workInProgressRootExitStatus === RootIncomplete) {
workInProgressRootExitStatus = RootCompleted;
}
return null;
}
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderExpirationTime: ExpirationTime,
): Fiber | null {
// 获取待更新 props
const newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
// 0
case FunctionComponent:
return null;
// 3
case HostRoot: {
updateHostContainer(workInProgress);
return null;
}
// 5
case HostComponent: {
// 获取 rootDOM 节点 <div id="root"></div>
const rootContainerInstance = getRootHostContainer();
// 节点的具体的类型 div span ...
const type = workInProgress.type;
// false current = null
if (current !== null && workInProgress.stateNode != null) {
// 初始渲染不执行
} else {
// false
if (wasHydrated) {
// 初始渲染不执行
} else {
// 创建节点实例对象 <div></div> <span></span>
let instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
/**
* 将所有的子级追加到父级中
* instance 为父级
* workInProgress.child 为子级
*/
appendAllChildren(instance, workInProgress, false, false);
// 为 Fiber 对象添加 stateNode 属性
workInProgress.stateNode = instance;
}
// 处理 ref DOM 引用
if (workInProgress.ref !== null) {
markRef(workInProgress);
}
}
return null;
}
}
}
appendAllChildren = function (
parent: Instance,
workInProgress: Fiber,
needsVisibilityToggle: boolean,
isHidden: boolean,
) {
// 获取子级
let node = workInProgress.child;
// 如果子级不为空 执行循环
while (node !== null) {
// 如果 node 是普通 ReactElement 或者为文本
if (node.tag === HostComponent || node.tag === HostText) {
// 将子级追加到父级中
appendInitialChild(parent, node.stateNode);
} else if (node.child !== null) {
// 如果 node 不是普通 ReactElement 又不是文本
// 将 node 视为组件, 组件本身不能转换为真实 DOM 元素
// 获取到组件的第一个子元素, 继续执行循环
node.child.return = node;
node = node.child;
continue;
}
// 如果 node 和 workInProgress 是同一个节点
// 说明 node 已经退回到父级 终止循环
// 说明此时所有子级都已经追加到父级中了
if (node === workInProgress) {
return;
}
// 处理子级节点的兄弟节点
while (node.sibling === null) {
// 如果节点没有父级或者节点的父级是自己, 退出循环
// 说明此时所有子级都已经追加到父级中了
if (node.return === null || node.return === workInProgress) {
return;
}
// 更新 node
node = node.return;
}
// 更新父级 方便回退
node.sibling.return = node.return;
// 将 node 更新为下一个兄弟节点
node = node.sibling;
}
};
4 提交阶段
workInProgress树构建完毕之后,将workInProgress树赋值给root.finishedwork,然后进入到commit阶段。
root.finishedWork = (root.current.alternate: any);
root.finishedExpirationTime = expirationTime;
finishSyncRender(root);
commit阶段不可被打断,将以最高优先级执行任务。
function commitRoot(root) {
// 获取任务优先级 97 => 普通优先级
const renderPriorityLevel = getCurrentPriorityLevel();
// 使用最高优先级执行当前任务, 因为 commit 阶段不可以被打断
// ImmediatePriority, 优先级为 99, 最高优先级
runWithPriority(
ImmediatePriority,
commitRootImpl.bind(null, root, renderPriorityLevel),
);
return null;
}
整个commit阶段分为三个子阶段,通过三个while循环来完成。
第一个子阶段(before mutation 阶段,执行DOM操作前),将调用类组件的
getSnapshotBeforeUpdate生命周期函数。
// commit 阶段的第一个子阶段
// 调用类组件的 getSnapshotBeforeUpdate 生命周期函数
function commitBeforeMutationEffects() {
// 循环 effect 链
while (nextEffect !== null) {
// nextEffect 是 effect 链上从 firstEffect 到 lastEffec 的每一个需要commit的 fiber 对象
// 初始化渲染第一个 nextEffect 为 App 组件
// effectTag => 3
const effectTag = nextEffect.effectTag;
// console.log(effectTag);
// nextEffect = null;
// return;
// 如果 fiber 对象中里有 Snapshot 这个 effectTag 的话
// Snapshot 和更新有关系 初始化渲染 不执行
// false
if ((effectTag & Snapshot) !== NoEffect) {
// 获取当前 fiber 节点
const current = nextEffect.alternate;
// 当 nextEffect 上有 Snapshot 这个 effectTag 时
// 执行以下方法, 主要是类组件调用 getSnapshotBeforeUpdate 生命周期函数
commitBeforeMutationEffectOnFiber(current, nextEffect);
}
// 更新循环条件
nextEffect = nextEffect.nextEffect;
}
}
第二个子阶段(mutation 阶段,执行 DOM 操作)
// commit 阶段的第二个子阶段
// 根据 effectTag 执行 DOM 操作
function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {
// 循环 effect 链
while (nextEffect !== null) {
// 获取 effectTag
// 初始渲染第一次循环为 App 组件
// 即将根组件及内部所有内容一次性添加到页面中
const effectTag = nextEffect.effectTag;
// 根据 effectTag 分别处理
let primaryEffectTag =
effectTag & (Placement | Update | Deletion | Hydrating);
// 匹配 effectTag
// 初始渲染 primaryEffectTag 为 2 匹配到 Placement
switch (primaryEffectTag) {
// 针对该节点及子节点进行插入操作
case Placement: {
commitPlacement(nextEffect);
// effectTag 从 3 变为 1
// 从 effect 标签中清除 "placement" 重置 effectTag 值
// 以便我们知道在调用诸如componentDidMount之类的任何生命周期之前已将其插入。
nextEffect.effectTag &= ~Placement;
break;
}
// 插入并更新 DOM
case PlacementAndUpdate: {
// 插入
commitPlacement(nextEffect);
nextEffect.effectTag &= ~Placement;
// 更新
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 服务器端渲染
case Hydrating: {
nextEffect.effectTag &= ~Hydrating;
break;
}
// 服务器端渲染
case HydratingAndUpdate: {
nextEffect.effectTag &= ~Hydrating;
// Update
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 更新 DOM
case Update: {
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
// 删除 DOM
case Deletion: {
commitDeletion(root, nextEffect, renderPriorityLevel);
break;
}
}
nextEffect = nextEffect.nextEffect;
}
}
// 挂载 DOM 元素
function commitPlacement(finishedWork: Fiber): void {
// finishedWork 初始化渲染时为根组件 Fiber 对象
// 获取非组件父级 Fiber 对象
// 初始渲染时为 <div id="root"></div>
const parentFiber = getHostParentFiber(finishedWork);
// 存储真正的父级 DOM 节点对象
let parent;
// 是否为渲染容器
// 渲染容器和普通react元素的主要区别在于是否需要特殊处理注释节点
let isContainer;
// 获取父级 DOM 节点对象
// 但是初始渲染时 rootFiber 对象中的 stateNode 存储的是 FiberRoot
const parentStateNode = parentFiber.stateNode;
// 判断父节点的类型
// 初始渲染时是 hostRoot 3
switch (parentFiber.tag) {
case HostComponent:
parent = parentStateNode;
isContainer = false;
break;
case HostRoot:
// 获取真正的 DOM 节点对象
// <div id="root"></div>
parent = parentStateNode.containerInfo;
// 是 container 容器
isContainer = true;
break;
case HostPortal:
parent = parentStateNode.containerInfo;
isContainer = true;
break;
case FundamentalComponent:
if (enableFundamentalAPI) {
parent = parentStateNode.instance;
isContainer = false;
}
}
// 查看当前节点是否有下一个兄弟节点
// 有, 执行 insertBefore
// 没有, 执行 appendChild
const before = getHostSibling(finishedWork);
// 渲染容器
if (isContainer) {
// 向父节点中追加节点 或者 将子节点插入到 before 节点的前面
insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
} else {
// 非渲染容器
// 向父节点中追加节点 或者 将子节点插入到 before 节点的前面
insertOrAppendPlacementNode(finishedWork, before, parent);
}
}
第三个子阶段(layout 阶段,执行 DOM 操作后),调用类组件生命周期函数,以及函数组件useEffect传入的函数。
// commit 阶段的第三个子阶段
function commitLayoutEffects(
root: FiberRoot,
committedExpirationTime: ExpirationTime,
) {
while (nextEffect !== null) {
// 此时 effectTag 已经被重置为 1, 表示 DOM 操作已经完成
const effectTag = nextEffect.effectTag;
// 调用生命周期函数和钩子函数
// 前提是类组件中调用了生命周期函数 或者函数组件中调用了 useEffect
if (effectTag & (Update | Callback)) {
// 类组件处理生命周期函数
// 函数组件处理钩子函数
commitLayoutEffectOnFiber(root, current,nextEffect, committedExpirationTime);
}
// 更新循环条件
nextEffect = nextEffect.nextEffect;
}
}