乐趣区

关于react.js:react源码解析6legacy模式和concurrent模式

react 源码解析 6.legacy 模式和 concurrent 模式

视频解说(高效学习):进入学习

往期文章:

1. 开篇介绍和面试题

2.react 的设计理念

3.react 源码架构

4. 源码目录构造和调试

5.jsx& 外围 api

6.legacy 和 concurrent 模式入口函数

7.Fiber 架构

8.render 阶段

9.diff 算法

10.commit 阶段

11. 生命周期

12. 状态更新流程

13.hooks 源码

14. 手写 hooks

15.scheduler&Lane

16.concurrent 模式

17.context

18 事件零碎

19. 手写迷你版 react

20. 总结 & 第一章的面试题解答

react 启动的模式

react 有 3 种模式进入主体函数的入口,咱们能够从 react 官网文档 应用 Concurrent 模式(实验性)中比照三种模式:

  • legacy 模式: ReactDOM.render(<App />, rootNode)。这是以后 React app 应用的形式。以后没有打算删除本模式,然而这个模式可能不反对这些新性能。
  • blocking 模式: ReactDOM.createBlockingRoot(rootNode).render(<App />)。目前正在试验中。作为迁徙到 concurrent 模式的第一个步骤。
  • concurrent 模式: ReactDOM.createRoot(rootNode).render(<App />)。目前在试验中,将来稳固之后,打算作为 React 的默认开发模式。这个模式开启了 所有的 新性能。

个性比照:

legacy 模式在合成事件中有主动批处理的性能,但仅限于一个浏览器工作。非 React 事件想应用这个性能必须应用 unstable_batchedUpdates。在 blocking 模式和 concurrent 模式下,所有的 setState 在默认状况下都是批处理的。会在开发中收回正告

不同模式在 react 运行时的含意

legacy 模式是咱们罕用的,它构建 dom 的过程是同步的,所以在 render 的 reconciler 中,如果 diff 的过程特地耗时,那么导致的后果就是 js 始终阻塞高优先级的工作(例如用户的点击事件),体现为页面的卡顿,无奈响应。

concurrent Mode 是 react 将来的模式,它用工夫片调度实现了异步可中断的工作,依据设施性能的不同,工夫片的长度也不一样,在每个工夫片中,如果工作到了过期工夫,就会被动让出线程给高优先级的工作。这部分将在第 15 节 scheduler&lane 模型。

两种模式函数次要执行过程

1. 次要执行流程:

2.具体函数调用过程

用 demo_0 跟着视频调试更加清晰,黄色局部是次要工作是创立 fiberRootNode 和 rootFiber,红色局部是创立 Update,蓝色局部是调度 render 阶段的入口函数

3.legacy 模式:

  • render 调用 legacyRenderSubtreeIntoContainer,最初 createRootImpl 会调用到 createFiberRoot 创立 fiberRootNode, 而后调用 createHostRootFiber 创立 rootFiber,其中 fiberRootNode 是整个我的项目的的根节点,rootFiber 是以后利用挂在的节点,也就是 ReactDOM.render 调用后的根节点

    // 最上层的节点是整个我的项目的根节点 fiberRootNode
    ReactDOM.render(<App />, document.getElementById("root"));//rootFiber
    ReactDOM.render(<App />, document.getElementById("root"));//rootFiber

  • 创立完 Fiber 节点后,legacyRenderSubtreeIntoContainer 调用 updateContainer 创立创立 Update 对象挂载到 updateQueue 的环形链表上,而后执行 scheduleUpdateOnFiber 调用 performSyncWorkOnRoot 进入 render 阶段和 commit 阶段

4.concurrent 模式:

  • createRoot 调用 createRootImpl 创立 fiberRootNode 和 rootNode
  • 创立完 Fiber 节点后,调用 ReactDOMRoot.prototype.render 执行 updateContainer,而后 scheduleUpdateOnFiber 异步调度 performConcurrentWorkOnRoot 进入 render 阶段和 commit 阶段

5.legacy 模式次要函数正文

function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
  //...
  var root = container._reactRootContainer;
  var fiberRoot;

  if (!root) {
    // 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);
      };
    } 


    unbatchedUpdates(function () {updateContainer(children, fiberRoot, parentComponent, callback);// 创立 update 入口
    });
  } else {
    // update 时
    fiberRoot = root._internalRoot;

    if (typeof callback === 'function') {// 解决回调
      var _originalCallback = callback;

      callback = function () {var instance = getPublicRootInstance(fiberRoot);

        _originalCallback.call(instance);
      };
    } 
    
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
}
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {var root = new FiberRootNode(containerInfo, tag, hydrate);// 创立 fiberRootNode
  const uninitializedFiber = createHostRootFiber(tag);// 创立 rootFiber
  //rootFiber 和 fiberRootNode 连贯
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;
  // 创立 updateQueue
  initializeUpdateQueue(uninitializedFiber);
  return root;
}

// 对于 HostRoot 或者 ClassComponent 会应用 initializeUpdateQueue 创立 updateQueue,而后将 updateQueue 挂载到 fiber 节点上
export function initializeUpdateQueue<State>(fiber: Fiber): void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,// 初始 state,前面会基于这个 state,依据 Update 计算新的 state
    firstBaseUpdate: null,//Update 造成的链表的头
    lastBaseUpdate: null,//Update 造成的链表的尾
        // 新产生的 update 会以单向环状链表保留在 shared.pending 上,计算 state 的时候会剪开这个环状链表,并且连贯在              //lastBaseUpdate 后
    shared: {pending: null,},
    effects: null,
  };
  fiber.updateQueue = queue;
}
function updateContainer(element, container, parentComponent, callback) {var lane = requestUpdateLane(current$1);// 获取以后可用 lane 在 12 章解说
  var update = createUpdate(eventTime, lane); // 创立 update

  update.payload = {element: element//jsx};

  enqueueUpdate(current$1, update);//update 入队
  scheduleUpdateOnFiber(current$1, lane, eventTime);// 调度 update
  return lane;
}
function scheduleUpdateOnFiber(fiber, lane, eventTime) {if (lane === SyncLane) {// 同步 lane 对应 legacy 模式
    //...
    performSyncWorkOnRoot(root);//render 阶段的终点 render 在第 6 章解说
  } else {//concurrent 模式
    //...
    ensureRootIsScheduled(root, eventTime);// 确保 root 被调度
  } 
}

6.concurrent 次要函数正文:

function ensureRootIsScheduled(root, currentTime) {
  //...
  var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); // 计算 nextLanes

  //...

 // 将 lane 的优先级转换成 schduler 的优先级
  var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);
  // 以 schedulerPriorityLevel 的优先级执行 performConcurrentWorkOnRoot 也就是 concurrent 模式的终点
  newCallbackNode =       scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root));
}

7. 两种模式的不同点:

  1. createRootImpl 中传入的第二个参数不一样 一个是 LegacyRoot 一个是 ConcurrentRoot
  2. requestUpdateLane 中获取的 lane 的优先级不同
  3. 在函数 scheduleUpdateOnFiber 中依据不同优先级进入不同分支,legacy 模式进入 performSyncWorkOnRoot,concurrent 模式会异步调度 performConcurrentWorkOnRoot
退出移动版