这里以React18的 Concurrent 并发模式作为基础
js
ReactDOM.createRoot(rootNode).render(<App />)
createRoot被调用时发生了什么?
通过观察如下调用堆栈我们可以看到具体的过程:
执行的顺序从createRoot
函数开始:
js
export function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,
): RootType {
// 省略掉一些处理createRoot options的逻辑
const root = createContainer(
container,
ConcurrentRoot,
null,
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onUncaughtError,
onCaughtError,
onRecoverableError,
transitionCallbacks,
);
return new ReactDOMRoot(root);
}
可以看到在createRoot
中会调用createContainer
函数
js
function createContainer(containerInfo, tag, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks) {
var hydrate = false;
var initialChildren = null;
return createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError);
}
在createContainer
函数 中会调用createFiberRoot
js
function createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride,
identifierPrefix, onRecoverableError, transitionCallbacks) {
var root = new FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError);
var uninitializedFiber = createHostRootFiber(tag, isStrictMode);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
{
var _initialState = {
element: initialChildren,
isDehydrated: hydrate,
cache: null,
// not enabled yet
transitions: null,
pendingSuspenseBoundaries: null
};
uninitializedFiber.memoizedState = _initialState;
}
initializeUpdateQueue(uninitializedFiber);
return root;
}
在createFiberRoot
中会有如下过程
- 创建一个 FiberRootNode 的实例
FiberRoot
作为整个React应用的根节点 - 调用
createHostRootFiber
创建 一个尚未初始化的Fiber树 - 将
FiberRoot.current
设置为 HostRootFiber 将HostRootFiber
的 stateNode 设置为 FiberRoot在二者之间建立联系 - 使用
initializeUpdateQueue
初始化更新队列 - 返回根节点
ps:更新队列是一个内部的数据结构,用于管理组件的状态更新和上下文变化。它负责跟踪组件的更新,并将它们排队以便在合适的时机批量处理。
root.render()
上一步中createRoot
创建的root对象prototype
上的方法被调用:
js
ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =
function (children: ReactNodeList): void {
const root = this._internalRoot;
updateContainer(children, root, null, null);
};
进入updateContainer
,updateContainer
中处理了lane
优先级相关信息,调用了updateContainerImpl
进行后续处理,updateContainer
相当于ReactDOM和reconciler的桥梁。
js
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): Lane {
const current = container.current;
const lane = requestUpdateLane(current);
updateContainerImpl(
current,
lane,
element,
container,
parentComponent,
callback,
);
return lane;
}
updateContainerImpl
:
- 调用
enqueueUpdate
函数将更新对象到根 Fiber 节点(rootFiber
)上,并指定更新的优先级车道(lane
) - 调用
scheduleUpdateOnFiber
函数将更新调度到根 Fiber 节点上,这将触发协调算法(reconciliation)
js
function updateContainerImpl(
rootFiber: Fiber,
lane: Lane,
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): void {
if (enableSchedulingProfiler) {
markRenderScheduled(lane);
}
const context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
const update = createUpdate(lane);
update.payload = {element};
callback = callback === undefined ? null : callback;
if (callback !== null) {
update.callback = callback;
}
const root = enqueueUpdate(rootFiber, update, lane);
if (root !== null) {
startUpdateTimerByLane(lane);
// 进入reconciler
scheduleUpdateOnFiber(root, rootFiber, lane);
entangleTransitions(root, rootFiber, lane);
}
}
调用之后会进入调度以及协调的过程,相关内容在后续的文章中更新;
总结
本文解释了React应用启动入口ReactDOM.createRoot
和root.render()
方法的内部执行过程。