1、逻辑分层

  • scheduleUpdateOnFiber方法为调度更新
  • performSyncWorkOnRoot为开启render阶段(render阶段)
  • commitRoot为真实Dom渲染过程(commit阶段)

    ReactDOM.render可以我分为三个阶段:

  1. 初始化阶段
  2. render阶段
  3. commit阶段

    2、初始化阶段

初始化主要干的事就是完成 Fiber 树中基本实体的创建,具体请移步查看源码阅读篇。
八、render阶段

2.1 主体逻辑链路

主要逻辑代码如下:

  1. function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
  2. // container 对应的是我们传入的真实 DOM 对象
  3. var root = container._reactRootContainer;
  4. // 初始化 fiberRoot 对象
  5. var fiberRoot;
  6. // DOM 对象本身不存在 _reactRootContainer 属性,因此 root 为空
  7. if (!root) {
  8. // 若 root 为空,则初始化 _reactRootContainer,并将其值赋值给 root
  9. root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
  10. // legacyCreateRootFromDOMContainer 创建出的对象会有一个 _internalRoot 属性,将其赋值给 fiberRoot
  11. fiberRoot = root._internalRoot;
  12. // 这里处理的是 ReactDOM.render 入参中的回调函数,你了解即可
  13. if (typeof callback === 'function') {
  14. // ...省略
  15. }
  16. // 进入 unbatchedUpdates 方法
  17. unbatchedUpdates(function () {
  18. updateContainer(children, fiberRoot, parentComponent, callback);
  19. });
  20. } else {
  21. // ...省略
  22. updateContainer(children, fiberRoot, parentComponent, callback);
  23. }
  24. return getPublicRootInstance(fiberRoot);
  25. }

上面代码主要逻辑链路:
image.png

2.2 root拆解:

image.png
这段代码其实就是做了这件事:

  • 创建 FiberRoot 和 rootFiber
  • fiberRoot的current属性指向rootFiber,
  • rootFiber的stateNode属性执行fiberRoot

image.png

fiberRoot 的关联对象是真实 DOM 的容器节点;而 rootFiber 则作为虚拟 DOM 的根节点存在。这两个节点,将是后续整棵 Fiber 树构建的起点。

2.3 unbatchedUpdates方法拆解:

  1. unbatchedUpdates(function () {
  2. updateContainer(children, fiberRoot, parentComponent, callback);
  3. });

unbatchedUpdates方法:

  1. function unbatchedUpdates(fn, a) {
  2. // ...省略...
  3. try {
  4. // 重点在这里,直接调用了传入的回调函数 fn,对应当前链路中的 updateContainer 方法
  5. return fn(a);
  6. } finally {
  7. // ...省略...
  8. }
  9. }

updateContainer方法:

  1. function updateContainer(element, container, parentComponent, callback) {
  2. ......
  3. // 这是一个 event 相关的入参,此处不必关注
  4. var eventTime = requestEventTime();
  5. ......
  6. // 这是一个比较关键的入参,lane 表示优先级
  7. var lane = requestUpdateLane(current$1);
  8. // 结合 lane(优先级)信息,创建 update 对象,一个 update 对象意味着一个更新
  9. var update = createUpdate(eventTime, lane);
  10. // update 的 payload 对应的是一个 React 元素
  11. update.payload = {
  12. element: element
  13. };
  14. // 处理 callback,这个 callback 其实就是我们调用 ReactDOM.render 时传入的 callback
  15. callback = callback === undefined ? null : callback;
  16. if (callback !== null) {
  17. {
  18. if (typeof callback !== 'function') {
  19. error('render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback);
  20. }
  21. }
  22. update.callback = callback;
  23. }
  24. // 将 update 入队
  25. enqueueUpdate(current$1, update);
  26. // 调度 fiberRoot
  27. scheduleUpdateOnFiber(current$1, lane, eventTime);
  28. // 返回当前节点(fiberRoot)的优先级
  29. return lane;
  30. }

updateContainer最关键的事情可以总结为三件:

  1. 请求当前 Fiber 节点的 lane(优先级);
  2. 结合 lane(优先级),创建当前 Fiber 节点的 update 对象,并将其入队;
  3. 调度当前节点(rootFiber);

    3、React模式

  4. legacy 模式:

ReactDOM.render(, rootNode)。这是当前 React App 使用的方式,当前没有计划删除本模式,但是这个模式可能不支持这些新功能。

  1. blocking 模式:

ReactDOM.createBlockingRoot(rootNode).render()。目前正在实验中,作为迁移到 concurrent 模式的第一个步骤。

  1. concurrent 模式:

ReactDOM.createRoot(rootNode).render()。目前在实验中,未来稳定之后,打算作为 React 的默认开发模式,这个模式开启了所有的新功能。

我们目前常用的 ReactDOM.render 对应的是 legacy 模式,它实际触发的仍然是同步的渲染链路。

同步的 ReactDOM.render,异步的ReactDOM.createRoot。