1 )概述
ReactDOM.render
过程当中,创建了一个 ReactRoot
的对象ReactRoot
对象最主要承担了创建 FiberRoot
对象FiberRoot
对象
ReactDOM.render
传进去的第二个参数
2 ) 相关源码
DOMRenderer.createContainer
创建了 FiberRoot
对象// ReactDOM.js
function ReactRoot(
container: Container,
isConcurrent: boolean,
hydrate: boolean,
) {
const root = DOMRenderer.createContainer(container, isConcurrent, hydrate);
this._internalRoot = root;
}
// ReactFiberReconciler.js
export function createContainer(
containerInfo: Container,
isConcurrent: boolean,
hydrate: boolean,
): OpaqueRoot {
return createFiberRoot(containerInfo, isConcurrent, hydrate);
}
// ReactFiberRoot.js
export function createFiberRoot(
containerInfo: any,
isConcurrent: boolean,
hydrate: boolean,
): FiberRoot {
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
const uninitializedFiber = createHostRootFiber(isConcurrent);
let root;
if (enableSchedulerTracing) {
root = ({
// root 节点对应的Fiber对象(root节点也是可能会更新的),是 Root Fiber
// 后续整个应用都会有一个Fiber的树结构,任何一个ReactElement的节点都会对应一个Fiber对象
// ReactElement是一个树结构, Fiber对象也会有一个树结构
// 它是Fiber树结构的顶点
current: uninitializedFiber,
containerInfo: containerInfo, // ReactDOM.render 传进去的第二个参数 dom 节点
pendingChildren: null, // 只有在持久更新中会用到,也就是不支持增量更新的平台,react-dom中不会被用到,在server-render中会被用到,因为它需要输出很多字符串类型的html节点
// 以下的优先级是用来区分
// 1 ) 没有提交(committed)的任务
// 2 ) 没有提交的挂起任务
// 3 ) 没有提交的可能被挂起的任务
// 最老和最新的不确定是否会挂起的优先级 (所有任务进来一开始都是这个状态)
earliestPendingTime: NoWork,
latestPendingTime: NoWork,
// 最老和最新的在提交的时候被挂起的任务
earliestSuspendedTime: NoWork,
latestSuspendedTime: NoWork,
// 最新的通过一个promise被resolve并且可以重新尝试的优先级
latestPingedTime: NoWork,
didError: false, // 在renderRoot 出现无法处理的错误时会被设置为 true
pendingCommitExpirationTime: NoWork, // 正在等待提交的任务的 expirationTime
// 已经完成的任务的FiberRoot对象,如果你只有一个Root, 它永远只可能是这个Root对应的Fiber, 或者是 null
// 在 commit 阶段只会处理这个值对应的任务
// 用于记录在一次更新渲染过程当中完成了的更新任务, 因为整棵树中会存在各种不同的更新任务
// 每一次更新会渲染优先级最高的任务,优先级最高的任务渲染完成之后就是一个 finishedWork,标记在应用的Root上面
// 更新完了要把应用输出到Dom节点上面,输出的过程就是读取的这个 finishedWork 值
finishedWork: null,
// 在任务被挂起的时候通过setTimeout设置的返回内容
// 用来下一次如果有新的任务挂起时清理还没触发的timeout
// 例如:Suspense 组件功能, 在render function 中 throw 一个 Promise, 之后任务会被挂起
// 挂起之后可以渲染 Suspense 组件的 fallback, 等到 Promise resolve之后
// 它就会把 resolve之后的数据显示出来,这时候就会有一个 timeoutHandle来帮助我们记录超时的情况的
// 这个会在后续的更新流程中
timeoutHandle: noTimeout,
// 顶层 context 对象, 只有主动调用 `renderSubtreeIntoContainer` 时才会有用
// `renderSubtreeIntoContainer` 这个API出场率非常低,不常用
context: null,
pendingContext: null,
// 用来确定第一次渲染的时候是否需要融合
// 应用是否要跟原来存在的dom节点进行合并
hydrate,
// 当前root上剩余的过期时间
// 用来标记这一次更新渲染的时候要执行的是哪个优先级的任务
// 应用更新过程中会遍历到每一个节点,每个节点如果有更新,会有自己的一个 ExpirationTime
// root 上记录整个应用当中优先级最高的 ExpirationTime
// 在更新的过程中,会根据这个nextExpirationTimeToWorkOn变量去进行一个更新
// 就是说如果遍历到某个节点,发现自己的 ExpirationTime 比这个值要大,则说明它优先级要低
// 且还轮不到它来更新,就可以进行一个跳过,这就是这个变量的用处
nextExpirationTimeToWorkOn: NoWork,
// 当前更新对应的过期时间
// 用在我们调度的过程当中, 和 上面的 nextExpirationTimeToWorkOn 大部分时间都是相同的
// 但是也会有一定的区别
expirationTime: NoWork,
// 暂时不管这个
firstBatch: null,
// root之间关联的链表结构
// 这个属性是单向链表的属性
// 比如,dom节点有 root1 和 root2 两个div
// ReactDOM.render 把一个应用渲染在 root1 中,把另一个应用渲染在 root2 中
// 这时候会存在两个root, 它们在react中会用 nextScheduledRoot 这个属性进行一个串联
// 在整个react更新调度过程中,在这个链表中去找哪个root对应的优先级最高,执行那个root的更新
// 一个root里面又会有不同优先级的任务在里面,它整体的过程是非常的复杂
nextScheduledRoot: null,
interactionThreadID: unstable_getThreadID(),
memoizedInteractions: new Set(),
pendingInteractionMap: new Map(),
}: FiberRoot);
} else {
root = ({
current: uninitializedFiber,
containerInfo: containerInfo,
pendingChildren: null,
earliestPendingTime: NoWork,
latestPendingTime: NoWork,
earliestSuspendedTime: NoWork,
latestSuspendedTime: NoWork,
latestPingedTime: NoWork,
didError: false,
pendingCommitExpirationTime: NoWork,
finishedWork: null,
timeoutHandle: noTimeout,
context: null,
pendingContext: null,
hydrate,
nextExpirationTimeToWorkOn: NoWork,
expirationTime: NoWork,
firstBatch: null,
nextScheduledRoot: null,
}: BaseFiberRootProperties);
}
uninitializedFiber.stateNode = root;
// The reason for the way the Flow types are structured in this file,
// Is to avoid needing :any casts everywhere interaction tracing fields are used.
// Unfortunately that requires an :any cast for non-interaction tracing capable builds.
// $FlowFixMe Remove this :any cast and replace it with something better.
return ((root: any): FiberRoot);
}
3 )相关解析