本文作者为 360 奇舞团前端开发工程师
前言
本文中使用的
React
版本为18,在摘取代码的过程中删减了部分代码,具体以源代码为准。
在React 18
里,通过ReactDOM.createRoot
创建根节点。并且通过调用原型链上的render
来渲染。 本文主要是从以下两个方法来介绍展开。
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
一、createRoot()
createRoot
这个方法主要是用来创建FiberRoot
(全局唯一,保存全局状态)和RootFiber
(是应用里的第一个fiber对象),并将其关系关联起来。主要有以下过程:
校验
container
有效性,以及处理options
参数创建
FiberRoot
和rootFiber
,并关联起来事件代理
返回
ReactDOMRoot
实例
function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,
): RootType {
// 校验合法性,以及处理options参数,此处省略
if (!isValidContainer(container)) {
//...
}
// 调用 createFiberRoot,创建FiberRoot和rootFiber,并关联关系,最终返回FiberRoot。FiberRoot.current = rootFiber; rootFiber.stateNode = FiberRoot;
const root = createContainer(
container,
ConcurrentRoot,
null,
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onRecoverableError,
transitionCallbacks,
);
// 标记container和rootFiber关系 container['__reactContainer$' + randomKey] = root.current
markContainerAsRoot(root.current, container);
const rootContainerElement: Document | Element | DocumentFragment =
container.nodeType === COMMENT_NODE
? (container.parentNode: any)
: container;
listenToAllSupportedEvents(rootContainerElement); // 事件代理
// 实例化,挂载render,unmount方法
return new ReactDOMRoot(root); // this._internalRoot = root;
}
关系结构示意图
二、render()
render
主要是通过调用updateContainer
,将组件渲染在页面上。
ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render = function(
children: ReactNodeList,
): void {
const root = this._internalRoot;
if (root === null) {
throw new Error('Cannot update an unmounted root.');
}
updateContainer(children, root, null, null);
};
updateContainer
updateContainer
主要执行了以下步骤:
获取当前时间
eventTime
和任务优先级lane
,调用createUpdate
生成update
;执行
enqueueUpdate
将更新添加到更新队列里,并返回FiberRoot
;scheduleUpdateOnFiber
调度更新;
function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): Lane {
const current = container.current; // rootFiber
const eventTime = requestEventTime(); // 更新触发时间
const lane = requestUpdateLane(current); // 获取任务优先级
// ... context 处理
// 创建update:{eventTime, lane, tag: UpdateState // 更新类型, payload: null, callback: null, next: null, // 下一个更新}
const update = createUpdate(eventTime, lane);
update.payload = {element}; // element首次渲染时为App
callback = callback === undefined ? null : callback;
if (callback !== null) {
update.callback = callback;
}
const root = enqueueUpdate(current, update, lane); // 将update添加到concurrentQueues队列里,返回 FiberRoo