React源码解析(三) —— 应用启动过程

这里以React18的 Concurrent 并发模式作为基础

js 复制代码
ReactDOM.createRoot(rootNode).render(<App />)

createRoot被调用时发生了什么?

通过观察如下调用堆栈我们可以看到具体的过程:

执行的顺序从createRoot函数开始:

js 复制代码
export function createRoot(
  container: Element | Document | DocumentFragment,
  options?: CreateRootOptions,
): RootType {
 
  // 省略掉一些处理createRoot options的逻辑
  
  const root = createContainer(
    container,
    ConcurrentRoot,
    null,
    isStrictMode,
    concurrentUpdatesByDefaultOverride,
    identifierPrefix,
    onUncaughtError,
    onCaughtError,
    onRecoverableError,
    transitionCallbacks,
  );
  return new ReactDOMRoot(root);
}

可以看到在createRoot中会调用createContainer函数

js 复制代码
function createContainer(containerInfo, tag, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks) {
  var hydrate = false;
  var initialChildren = null;
  return createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError);
}

createContainer函数 中会调用createFiberRoot

js 复制代码
function createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, 
identifierPrefix, onRecoverableError, transitionCallbacks) {
  var root = new FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError);
  var uninitializedFiber = createHostRootFiber(tag, isStrictMode);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;
  {
    var _initialState = {
      element: initialChildren,
      isDehydrated: hydrate,
      cache: null,
      // not enabled yet
      transitions: null,
      pendingSuspenseBoundaries: null
    };
    uninitializedFiber.memoizedState = _initialState;
  }

  initializeUpdateQueue(uninitializedFiber);
  return root;
}

createFiberRoot中会有如下过程

  • 创建一个 FiberRootNode 的实例 FiberRoot作为整个React应用的根节点
  • 调用 createHostRootFiber 创建 一个尚未初始化的Fiber树
  • FiberRoot.current 设置为 HostRootFiber 将 HostRootFiber 的 stateNode 设置为 FiberRoot在二者之间建立联系
  • 使用initializeUpdateQueue初始化更新队列
  • 返回根节点

ps:更新队列是一个内部的数据结构,用于管理组件的状态更新和上下文变化。它负责跟踪组件的更新,并将它们排队以便在合适的时机批量处理。

root.render()

上一步中createRoot创建的root对象prototype上的方法被调用:

js 复制代码
ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =
  function (children: ReactNodeList): void {
    const root = this._internalRoot;
    updateContainer(children, root, null, null);
  };

进入updateContainer,updateContainer中处理了lane优先级相关信息,调用了updateContainerImpl进行后续处理,updateContainer相当于ReactDOM和reconciler的桥梁。

js 复制代码
export function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): Lane {
  const current = container.current;
  const lane = requestUpdateLane(current);
  updateContainerImpl(
    current,
    lane,
    element,
    container,
    parentComponent,
    callback,
  );
  return lane;
}

updateContainerImpl:

  • 调用 enqueueUpdate 函数将更新对象到根 Fiber 节点(rootFiber)上,并指定更新的优先级车道(lane
  • 调用 scheduleUpdateOnFiber 函数将更新调度到根 Fiber 节点上,这将触发协调算法(reconciliation)
js 复制代码
function updateContainerImpl(
  rootFiber: Fiber,
  lane: Lane,
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): void {

  if (enableSchedulingProfiler) {
    markRenderScheduled(lane);
  }

  const context = getContextForSubtree(parentComponent);
  if (container.context === null) {
    container.context = context;
  } else {
    container.pendingContext = context;
  }


  const update = createUpdate(lane);
  update.payload = {element};

  callback = callback === undefined ? null : callback;
  
  if (callback !== null) {
    update.callback = callback;
  }

  const root = enqueueUpdate(rootFiber, update, lane);
  
  if (root !== null) {
    startUpdateTimerByLane(lane);
    // 进入reconciler
    scheduleUpdateOnFiber(root, rootFiber, lane);
    entangleTransitions(root, rootFiber, lane);
  }
}

调用之后会进入调度以及协调的过程,相关内容在后续的文章中更新;

总结

本文解释了React应用启动入口ReactDOM.createRootroot.render()方法的内部执行过程。

相关推荐
securitor17 分钟前
【java】IP来源提取国家地址
java·前端·python
yqcoder1 小时前
NPM 包管理问题汇总
前端·npm·node.js
程序菜鸟营1 小时前
nvm安装详细教程(安装nvm、node、npm、cnpm、yarn及环境变量配置)
前端·npm·node.js
bsr19831 小时前
前端路由的hash模式和history模式
前端·history·hash·路由模式
杨过姑父2 小时前
ES6 简单练习笔记--变量申明
前端·笔记·es6
Jacob程序员2 小时前
leaflet绘制室内平面图
android·开发语言·javascript
Sunny_lxm2 小时前
<keep-alive> <component ></component> </keep-alive>缓存的组件实现组件,实现组件切换时每次都执行指定方法
前端·缓存·component·active
eguid_12 小时前
JavaScript图像处理,常用图像边缘检测算法简单介绍说明
javascript·图像处理·算法·计算机视觉
sunly_3 小时前
Flutter:自定义Tab切换,订单列表页tab,tab吸顶
开发语言·javascript·flutter
咔咔库奇3 小时前
【TypeScript】命名空间、模块、声明文件
前端·javascript·typescript