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);
}

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;
}

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');
};

然后我们再来看以下 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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值