• React源码分析3-render阶段(穿插scheduler和reconciler)


    本章将讲解 react 的核心阶段之一 —— render阶段,我们将探究以下部分内容的源码:

    • 更新任务的触发
    • 更新任务的创建
    • reconciler 过程同步和异步遍历及执行任务
    • scheduler 是如何实现帧空闲时间调度任务以及中断任务的

    触发更新

    触发更新的方式主要有以下几种:ReactDOM.rendersetStateforUpdate 以及 hooks 中的 useState 等,关于 hooks 的我们后面再详细讲解,这里先关注前三种情况。

    ReactDOM.render

    ReactDOM.render 作为 react 应用程序的入口函数,在页面首次渲染时便会触发,页面 dom 的首次创建,也属于触发 react 更新的一种情况。

    首先调用 legacyRenderSubtreeIntoContainer 函数,校验根节点 root 是否存在,若不存在,调用 legacyCreateRootFromDOMContainer 创建根节点 root、rootFiber 和 fiberRoot 并绑定它们之间的引用关系,然后调用 updateContainer 去非批量执行后面的更新流程;若存在,直接调用 updateContainer 去批量执行后面的更新流程:

    // packages/react-dom/src/client/ReactDOMLegacy.js
    
    function legacyRenderSubtreeIntoContainer(
      parentComponent: ?React$Component<any, any>,  children: ReactNodeList,  container: Container,  forceHydrate: boolean,  callback: ?Function,
    ) {
       
      // ...
      let root: RootType = (container._reactRootContainer: any);
      let fiberRoot;
      if (!root) {
       
        // 首次渲染时根节点不存在
        // 通过 legacyCreateRootFromDOMContainer 创建根节点、fiberRoot 和 rootFiber
        root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
          container,
          forceHydrate,
        );
        fiberRoot = root._internalRoot;
        if (typeof callback === 'function') {
       
          const originalCallback = callback;
          callback = function() {
       
            const instance = getPublicRootInstance(fiberRoot);
            originalCallback.call(instance);
          };
        }
        // 非批量执行更新流程
        unbatchedUpdates(() => {
       
          updateContainer(children, fiberRoot, parentComponent, callback);
        });
      } else {
       
        fiberRoot = root._internalRoot;
        if (typeof callback === 'function') {
       
          const originalCallback = callback;
          callback = function() {
       
            const instance = getPublicRootInstance(fiberRoot);
            originalCallback.call(instance);
          };
        }
        // 批量执行更新流程
        updateContainer(children, fiberRoot, parentComponent, callback);
      }
      return getPublicRootInstance(fiberRoot);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    updateContainer 函数中,主要做了以下几件事情:

    • requestEventTime:获取更新触发的时间
    • requestUpdateLane:获取当前任务优先级
    • createUpdate:创建更新
    • enqueueUpdate:将任务推进更新队列
    • scheduleUpdateOnFiber:调度更新
      关于这几个函数稍后会详细讲到
    // packages/react-dom/src/client/ReactDOMLegacy.js
    
    export function updateContainer(
      element: ReactNodeList,  container: OpaqueRoot,  parentComponent: ?React$Component<any, any>,  callback: ?Function,
    ): Lane {
       
      // ...
      const current = container.current;
      const eventTime = requestEventTime(); // 获取更新触发的时间
      // ...
      const lane = requestUpdateLane(current); // 获取任务优先级
    
      if (enableSchedulingProfiler) {
       
        markRenderScheduled(lane);
      }
    
      const context = getContextForSubtree(parentComponent);
      if (container.context === null) {
       
        container.context = context;
      } else {
       
        container.pendingContext = context;
      }
    
      // ...
    
      const update = createUpdate(eventTime, lane); // 创建更新任务
      update.payload = {
       element};
    
      callback = callback === undefined ? null : callback;
      if (callback !== null) {
       
        // ...
        update.callback = callback;
      }
    
      enqueueUpdate(current, update); // 将任务推入更新队列
      scheduleUpdateOnFiber(current, lane, eventTime); // schedule 进行调度
    
      return lane;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    setState

    setState 时类组件中我们最常用的修改状态的方法,状态修改会触发更新流程。

    class 组件在原型链上定义了 setState 方法,其调用了触发器 updater 上的 enqueueSetState 方法:

    // packages/react/src/ReactBaseClasses.js
    
    Component.prototype.setState = function(partialState, callback) {
       
      invariant(
        typeof partialState === 'object' ||
          typeof partialState === 'function' ||
          partialState == null,
        'setState(...): takes an object of state variables to update or a ' +
          'function which returns an object of state variables.',
      );
      this.updater.enqueueSetState(this, partialState, callback, 'setState');
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    然后我们再来看以下 updater 上定义的 enqueueSetState 方法,一看到这我们就了然了,和 updateContainer 方法中做的事情几乎一模一样,都是触发后续的更新调度。

    相关参考视频讲解:进入学习

    // packages/react-reconciler/src/ReactFiberClassComponent.old.js
    
    const classComponentUpdater = {
       
      isMounted,
      enqueueSetState(inst, payload, callback) {
       
        const fiber = getInstance(inst);
        const eventTime = requestEventTime(); // 获取更新触发的时间
        const lane = requestUpdateLane(fiber); // 获取任务优先级
    
        const update = createUpdate(eventTime, lane); // 创建更新任务
        update.payload = payload
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    2 用TensorFlow构建一个简单的神经网络
    js检索(捕获)exec的使用2
    习题:选择结构(二)
    Android安装过程二 系统进程中PackageInstallerSession对象的创建
    “第六十三天”
    【考研线代】二. 矩阵
    WMS是什么?怎么选择WMS?
    day37
    【Java 基础篇】Java网络编程实时数据流处理
    PriorityQueue的详解
  • 原文地址:https://blog.csdn.net/It_kc/article/details/127681760