Render阶段
可大致归为beginWork(递)和completeWork(归)两个阶段
1.beginWork流程(递)
- 建立节点的父子以及兄弟节点关联关系
child return sibling
属性- 给fiber节点打上
flag标记
(当前节点的flag)
渲染阶段结束
,fiberRootNode.finishwork=wip
,进入就断除
!
2.completeWork流程(归)主要执行任务:
1.
创建真实dom节点
,但是仍在内存中,未渲染到页面2.
处理flag与subtreeFlags
(标记
子树标识,用"|"
位运算处理)3.
建立真实DOM关系
,将子元素插入父元素中completeWork归工作完成,将建立
fiberRootNode.finishWork=wip
关系,当然进入
Commit阶段
commit工作前又会断掉此关系。(状态机,标识运行状态)
- 当commitMutationEffect(
commit执行完
),将dom渲染到页面中之后
root.current=finishedWork断开和老节点的联系
,指向新节点
bash
function commitRoot(root:FiberRootNode){
const finishedwork =root.finishedwork;
if(finishedwork === null){
return;
}
console.log('commit阶段开始',finishedwork);
root.finishedwork=null;
//判断是否存在3个子阶段需要执行的造作
// root flags root subtreerlags
const subtreeHaseffect=
(finishedwork.subtreeFlags & MutationMask)!== NoFlags;
const rootHasEffect:(finishedwork,flags & MutationMask) !== NoFlags;
if(subtreeHasEffectrootHasEffect){
// beforeMutation
//mutation placement
commitMutationEffects(finishedwork);
root.current =finishedwork;
// layout
}else {
root.current =finishedwork;
}
- 在渲染阶段的completeWork函数中已经构建真实DOM的关系,并对fiber树的每个节点进行了标识。标识包括两部分,一个是
flag
标识当前节点是否有DOM副作用操作
,另一个就是subtreeFlag
标识当前节点的子树是否有DOM副作用操作
!- 巧妙的双flag ,后续更改,虚拟DOM跟着有关?
commitMutationEffects函数详解
在React的Fiber架构中,commitMutationEffects
是一个内部函数,负责在"commit"阶段的"mutation"子阶段中执行DOM更新。这个函数是React渲染流程中的关键一环,它确保了React组件的状态变更能够正确地反映到真实的DOM结构上。
javascript
function commitMutationEffects(root, finishedWork, committedLanes) {
let nextEffect = finishedWork;
// 遍历 Fiber 树,处理副作用
while (nextEffect !== null) {
const deletions = nextEffect.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
commitDeletion(root, childToDelete);
}
}
if (nextEffect.subtreeFlags & MutationMask) {
const child = nextEffect.child;
if (child !== null) {
child.return = nextEffect;
nextEffect = child;
continue;
}
}
commitMutationEffectsOnFiber(nextEffect, root, committedLanes);
const sibling = nextEffect.sibling;
if (sibling !== null) {
sibling.return = nextEffect.return;
nextEffect = sibling;
continue;
}
nextEffect = nextEffect.return;
}
}
function commitMutationEffectsOnFiber(finishedWork, root, committedLanes) {
const flags = finishedWork.flags;
// 插入新节点
if (flags & Placement) {
commitPlacement(finishedWork);
finishedWork.flags &= ~Placement; // 清除 Placement 标记
}
// 更新节点
if (flags & Update) {
commitUpdate(finishedWork);
}
// 删除节点
if (flags & Deletion) {
commitDeletion(root, finishedWork);
}
// 处理 refs
if (flags & Ref) {
commitAttachRef(finishedWork);
}
}
function commitPlacement(finishedWork) {
const parentFiber = getHostParentFiber(finishedWork);
const parent = getHostParent(parentFiber);
if (parent !== null) {
const before = getHostSibling(finishedWork);
appendChildToContainer(parent, finishedWork.stateNode, before);
}
}
function commitDeletion(root, childToDelete) {
// 清理 refs
if (childToDelete.flags & Ref) {
commitDetachRef(childToDelete);
}
// 卸载子组件
unmountHostComponents(childToDelete);
// 如果节点有副作用清理函数,执行它们
const alternate = childToDelete.alternate;
if (alternate !== null) {
const finishedEffect = alternate.lastEffect;
if (finishedEffect !== null) {
// 执行 Effect 的销毁函数
commitPassiveUnmountEffects(finishedEffect);
}
}
}
commitMutationEffects的作用
commitMutationEffects
的主要任务是遍历Fiber树中的effect list(副作用列表),并执行与DOM操作相关的副作用
。这些副作用可能包括:
- 插入新元素:当React组件被添加到DOM中时,需要创建新的DOM节点并将其插入到正确的位置。
- 删除旧元素:当React组件被移除时,需要从DOM中删除对应的节点。
- 更新元素属性:当React组件的属性(props)或状态(state)发生变化时,需要更新DOM节点的属性以反映这些变化。
- 文本内容更新:对于文本节点,当文本内容发生变化时,需要更新DOM节点的文本内容。
执行过程
在执行commitMutationEffects
时,React会按照Fiber树的遍历顺序,逐个处理effect list中的每个effect。对于每个effect,React会根据其类型(如插入、删除、更新等)执行相应的DOM操作。
- 遍历 Fiber 树
React 使用深度优先的方式遍历 Fiber 树,对每一个 Fiber 节点检查是否有需要执行的副作用(Effect)。 - 处理不同的副作用标记
每个 Fiber 节点有一个 flags 属性,表示需要处理的副作用类型。在 commitMutationEffectsOnFiber 中,处理了以下常见的 flags:- Placement: 表示需要将节点插入 DOM。
- Update: 表示节点属性或样式更新。
- Deletion: 表示需要从 DOM 中移除节点。
- 特殊处理
某些类型的节点(如 Class 组件和函数组件)需要处理额外的逻辑,例如:- 调用生命周期方法(如 componentWillUnmount)。
- 清理资源(如 Effect 的销毁函数)。
- 递归子节点
在处理完当前节点的副作用后,继续递归处理其子节点,直到整个 Fiber 树被遍历完成。
注意事项
- 不可中断性 :与render阶段不同,
commit阶段是同步且不可中断的
。这意味着一旦开始执行commitMutationEffects
,它将一直执行到完成,而不会被其他任务中断。 - 性能优化 :尽管commit阶段需要执行DOM操作,但React通过一些优化策略来减少不必要的DOM更新。例如,React使用
虚拟DOM来比较新旧Fiber树之间的差异,并只更新那些实际发生变化的DOM节点
。 - 副作用处理 :除了DOM操作外,
commitMutationEffects
还可能处理其他类型的副作用,如类组件的生命周期方法调用(如componentDidUpdate
)和函数组件的useEffect
回调执行。然而,这些副作用通常会在commit阶段的不同子阶段中处理。
关键点说明
-
标记系统(Flags): React 使用 flags 属性来标记需要处理的副作用,比如
Placement、Update、Deletion。
-
子树标记(SubtreeFlags): subtreeFlags 用于快速判断某个子树是否有副作用,可以跳过不需要处理的部分。
-
递归与遍历: nextEffect 的遍历逻辑是 React 树递归的重要模式,支持深度优先遍历整个 Fiber 树。
-
辅助函数: 像 commitPlacement、commitUpdate 和 commitDeletion
是副作用执行的关键,它们最终会操作 DOM 或调用组件的生命
周期方法。
总结
commitMutationEffects
是React Fiber架构中负责执行DOM更新的关键函数。它确保了React组件的状态变更能够正确地反映到真实的DOM结构上,并通过一些优化策略来提高性能。了解这个函数的工作原理有助于深入理解React的渲染流程和性能优化策略。