ReactElement, Fiber, DOM 三者的关系
- 所有采用
jsx语法书写的节点, 都会被编译器转换, 最终会使用jsx()的方式, 创建出来一个与之对应的ReactElement对象。 fiber对象是通过ReactElement对象进行创建的, 多个fiber对象构成了一棵fiber树,fiber树是构造DOM树的数据模型,fiber树的任何改动, 最后都体现到DOM树。dom对象也就是渲染在页面上的dom元素。
三者的关系:
双缓冲技术
current
指向当前已经渲染完成的fiber树
workInProgress
指向正在构建中的fiber树
构造完成并渲染, 就切换
fiberRoot.current指针,让current指针指向workinprogress。
组件类型
每一种vdom都有对应的tag类型,创建时根据ReactElement的type选择。
export const FunctionComponent = 0; //函数组件
export const ClassComponent = 1; //类组件
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; //根节点
export const HostPortal = 4;
export const HostComponent = 5; //原生dom节点
export const HostText = 6; //纯文本节点
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const ScopeComponent = 21;
export const OffscreenComponent = 22;
export const LegacyHiddenComponent = 23;
export const CacheComponent = 24;
export const TracingMarkerComponent = 25;
workloop循环
整个fiber树构造是一个深度优先遍历(可参考React 算法之深度优先遍历)
在深度优先遍历中, 每个fiber节点都会经历2个阶段:
- 探寻阶段
beginWork - 回溯阶段
completeWork
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress)
}
}
function performUnitOfWork(unitofWork) {
//获取新fiber对应的老fiber
const current = unitofWork.alternate
//完成当前fiber的子fiber联表构建后
const next = beginWork(current, unitofWork, workInProgressRootRenderLanes)
unitofWork.memoizedProps = unitofWork.pendingProps
if (next === null) {
//没有子节点,表示当前节点构建完成
completeUnitOfWork(unitofWork)
} else {
// 如果有子节点,让子节点成为工作单元
workInProgress = next
}
}
beginWork
针对所有的 Fiber 类型, 其中的每一个 case 处理一种 Fiber 类型。
主要逻辑:
- 根据
ReactElement对象创建所有的fiber节点, 最终构造出fiber树形结构(设置return和sibling指针) - 设置
fiber.flags(二进制形式变量, 用来标记fiber节点 的增,删,改状态, 等待completeWork阶段处理) - 设置
fiber.stateNode局部状态(如Class类型节点:fiber.stateNode=new Class())
export function beginWork(current, workInProgress, renderLanes) {
// logger(' '.repeat(indent.number) + 'beginWork', workInProgress)
// indent.number += 2
switch (workInProgress.tag) {
case HostRoot:
return updateHostRoot(current, workInProgress, renderLanes)
case HostComponent:
return updateHostComponent(current, workInProgress, renderLanes)
case HostText:
return null
//初次挂载时,函数组件走这个
//mountIndeterminateComponent中设置workInProgress.tag = FunctionComponent
//更新时,函数组件走FunctionComponent
case IndeterminateComponent:
return mountIndeterminateComponent(
current,
workInProgress,
workInProgress.type,
renderLanes
)
case FunctionComponent:
const Component = workInProgress.type
const nextProps = workInProgress.pendingProps
return updateFunctionComponent(
current,
workInProgress,
Component,
nextProps,
renderLanes
)
default:
return null
}
}
complateWork
主要工作
-
调用
completeWork- 给
fiber节点(tag=HostComponent, HostText)创建 DOM 实例, 设置fiber.stateNode局部状态(如tag=HostComponent, HostText节点: fiber.stateNode 指向这个 DOM 实例). - 为 DOM 节点设置属性, 绑定事件(这里先说明有这个步骤, 详细的事件处理流程, 在
合成事件原理中详细说明). - 设置
fiber.flags标记
- 给
-
把当前
fiber对象的副作用队列(firstEffect和lastEffect)添加到父节点的副作用队列之后, 更新父节点的firstEffect和lastEffect指针. -
识别
beginWork阶段设置的fiber.flags, 判断当前fiber是否有副作用(增,删,改), 如果有, 需要将当前fiber加入到父节点的effects队列, 等待commit阶段处理.
function completeUnitOfWork(unitofWork) {
let completedWork = unitofWork
do {
const current = completedWork.alternate
const returnFiber = completedWork.return
//执行此fiber的完成工作
//如果是原生组件,创建真实的dom组件
completeWork(current, completedWork)
const siblingFiber = completedWork.sibling
//如果有弟弟,构建弟弟的fiber子链表
if (siblingFiber !== null) {
workInProgress = siblingFiber
return
}
//如果没有弟弟,说明这当前完成的就是父fiber的最后一个节点
//也就说明父fiber的子链表构建完成,也就是父fiber完成了
completedWork = returnFiber
workInProgress = completedWork
} while (completedWork !== null)
// 如果走到这里,说明整个fiber树全部构建完成
if (workInProgressRootExitStatus === RootInProgress) {
workInProgressRootExitStatus = RootCompleted
}
}
commitRoot
- 处理副作用队列(增删改).
- 调用渲染器, 输出最终结果(挂载真实dom).
function commitRootImpl(root) {
//获取新的构建好的fiber树的根节点
const { finishedWork } = root
workInProgressRoot = null
workInProgressRootRenderLanes = NoLanes
root.callbackNode = null
root.callbackPriority = null
// printFinishedWork(finishedWork)
if (
(finishedWork.subtreeFlags & Passive) !== NoFlags ||
(finishedWork.flags & Passive) !== NoFlags
) {
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true
scheduleCallback(NormalSchedulerPriority, flushPassiveEffects)
}
}
// console.log('开始commit~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
//subtreeHasEffects中包含添加或更新
const subtreeHasEffects =
(finishedWork.subtreeFlags & MutationMask) !== NoFlags
//flags中包含添加或更新
const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags
//如果自己有副作用或者子节点有副作用,就进行提交操作
if (subtreeHasEffects || rootHasEffect) {
// console.log(
// 'dom执行变更 - commitMutationEffectsOnFiber~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
// )
//dom执行变更之后
commitMutationEffectsOnFiber(finishedWork, root)
console.log(
'dom执行变更后 - commitLayoutEffects~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
)
// 执行layoutEffect
commitLayoutEffects(finishedWork, root)
if (rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = false
rootWithPendingPassiveEffects = root
}
}
// dom变更后,让root的current指向新的fiber树
root.current = finishedWork
// root.pendingLanes = 16
// ensureRootIsScheduled(root)
}
本文参考和引用文章列表:(如有侵权,请告知删除)
github.com/7kms/react-…