# 创建根节点
# legacy和concurrent模式
leagcy模式入口函数是reactDom.render,而concurrent模式入口函数是react.createRoot。两者差别在于leagcy模式构建dom的过程是同步的。concurrent模式构建dom的过程是异步的
ReactDOM.render(<App />, rootEl);
// leagcy模式
ReactDOM.unstable_createRoot(rootEl).render(<App />);
// concurrent模式,因为还是实验阶段所以要加上unstable_
# leagcy模式源码
leagcy模式入口函数是reactDom.render。从function render开始。建议用一个react app搜索 function render 断点调试。文字实在难以描述
leagcy模式主要源码如下。主要流程是创建rootFiber和FiberRootNode
function render(element, container, callback) { // leagcy模式入口函数
//。。。
return legacyRenderSubtreeIntoContainer(null, element, container, false, callback); // 调用legacyRenderSubtreeIntoContainer
}
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
// ...
var root = container._reactRootContainer;
var fiberRoot;
if (!root) { // mount阶段root不存在需要创建root
// Initial mount
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate); // 调用这个函数创建root
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
var originalCallback = callback;
callback = function () {
var instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
} // Initial mount should not be batched.
unbatchedUpdates(function () { // 非批量更新形式调用
updateContainer(children, fiberRoot, parentComponent, callback); // 两种模式都会用到的方法详情见下面
});
} else { // update时走这个。代码暂时不贴了
//...
} // Update
updateContainer(children, fiberRoot, parentComponent, callback);
}
return getPublicRootInstance(fiberRoot);
}
function legacyCreateRootFromDOMContainer(container, forceHydrate) {
var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // First clear any existing content.
if (!shouldHydrate) {
//... hydrate相关
}
//...
return createLegacyRoot(container, shouldHydrate ? { // 主要是这个
hydrate: true
} : undefined);
}
function createLegacyRoot(container, options) {
return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}
function ReactDOMBlockingRoot(container, tag, options) {
this._internalRoot = createRootImpl(container, tag, options); // 调用这个方法赋值this._internalRoot结合legacyRenderSubtreeIntoContainer这个返回值将成为rootFiber
}
function createRootImpl(container, tag, options) {
// ...
var root = createContainer(container, tag, hydrate); // 调用这个创建root
markContainerAsRoot(root.current, container);
var containerNodeType = container.nodeType;
{
var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
listenToAllSupportedEvents(rootContainerElement);
}
if (mutableSources) {
for (var i = 0; i < mutableSources.length; i++) {
var mutableSource = mutableSources[i];
registerMutableSourceForHydration(root, mutableSource);
}
}
return root;
}
function createContainer(containerInfo, tag, hydrate, hydrationCallbacks) {
return createFiberRoot(containerInfo, tag, hydrate); // 创建FiberRootNode和rootFiber两种模式都会调用这个方法,详情见下面
}
# concurrent模式源码
leagcy模式入口函数是reactDom.createRoot,从function createRoot开始。同样建议源码调试
function createRoot(container, options) {
// ...
return new ReactDOMRoot(container, options); // 主要是调用ReactDOMRoot
}
function ReactDOMRoot(container, options) {
this._internalRoot = createRootImpl(container, ConcurrentRoot, options); // 创建rootFiber
}
function createRootImpl(container, tag, options) {
var root = createContainer(container, tag, hydrate);
markContainerAsRoot(root.current, container); // 这个主要是把root的internalContainerInstanceKey属性设置为hostRoot
var containerNodeType = container.nodeType;
{
var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
listenToAllSupportedEvents(rootContainerElement);
}
if (mutableSources) {
for (var i = 0; i < mutableSources.length; i++) {
var mutableSource = mutableSources[i];
registerMutableSourceForHydration(root, mutableSource);
}
}
return root;
}
function createContainer(containerInfo, tag, hydrate, hydrationCallbacks) {
return createFiberRoot(containerInfo, tag, hydrate);
}
再创建完root节点后会通过ReactDOMRoot.prototype.render进入下一步
ReactDOMRoot.prototype.render = ReactDOMBlockingRoot.prototype.render = function (children) {
var root = this._internalRoot;
{
if (typeof arguments[1] === 'function') {
error('render(...): does not support the second callback argument. ' + 'To execute a side effect after rendering, declare it in a component body with useEffect().');
}
var container = root.containerInfo;
if (container.nodeType !== COMMENT_NODE) {
var hostInstance = findHostInstanceWithNoPortals(root.current);
if (hostInstance) {
if (hostInstance.parentNode !== container) {
error('render(...): It looks like the React-rendered content of the ' + 'root container was removed without using React. This is not ' + 'supported and will cause errors. Instead, call ' + "root.unmount() to empty a root's container.");
}
}
}
}
updateContainer(children, root, null, null);
};
# 都调用的方法
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
var root = new FiberRootNode(containerInfo, tag, hydrate); //创建FiberRootNode
var uninitializedFiber = createHostRootFiber(tag); // 创建rootFiber
root.current = uninitializedFiber; //FiberRootNode creent指向rootFiber详情
uninitializedFiber.stateNode = root; // 详情见Fiber双缓存
initializeUpdateQueue(uninitializedFiber); //初始化任务队列
return root;
}
到了这里根节点就创建完成了,此时的Fiber树为。注意此时的FiberRootNode和rootFiber只有零星的几个属性(看传参就能知道),真正的创建Fiber树在下面
下面两段代码只是单纯的说明两种模式启动方式的差异
function updateContainer(element, container, parentComponent, callback) {
var lane = requestUpdateLane(current$1);//获取当前可用lane后续再说
var update = createUpdate(eventTime, lane); //创建update
update.payload = {
element: element
};
enqueueUpdate(current$1, update);
scheduleUpdateOnFiber(current$1, lane, eventTime);
return lane;
}
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
if (lane === SyncLane) {//同步lane 对应legacy模式
//...
performSyncWorkOnRoot(root);//进入render阶段
} else {//concurrent模式
//...
ensureRootIsScheduled(root, eventTime);//进入render阶段
}
}
# 总结与区别
上面贴了两种模式代码(仅限于创建FiberRootNode和rootFiber)
1、createRootImpl中传入的第二个参数不一样 一个是LegacyRoot一个是ConcurrentRoot。这两个变量都是在全局中定义的
var LegacyRoot = 0;
var BlockingRoot = 1;
var ConcurrentRoot = 2;
2、 在updateContainer时所获取Lane模型不同。同时也就导致了两种模式进入render起点的方式不同,concurrent模是通过ensureRootIsScheduled异步进入的