# 创建根节点

# 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异步进入的