【React 原理探究实践】root.render 干了啥?——深入 render 函数

🧑‍💻 写在开头

点赞 + 收藏 = 学会 🤣

在上一篇文章里,我们讲了 React 的三大全局对象,以及 React 是如何启动的。但是你可能会问:

React 启动好了,怎么把 <App /> 这个组件挂上页面呢?

这篇文章就带你从源码角度,分析 root.render<App /> 挂载到 DOM 的完整原理。


🔎 高层回顾:React 启动 + 渲染 App 的总体脉络

React 应用中,渲染 <App /> 大致分为几步:

  1. 入口调用

    javascript 复制代码
    ReactDOM.createRoot(document.getElementById('root')).render(<App />);
    // 或 ReactDOM.render(<App />, document.getElementById('root'));

    也就是告诉 React:"把 <App /> 挂到这个 DOM 容器里。"

  2. 创建根 Fiber

    React 内部会创建一个 FiberRoot / HostRootFiber 节点,作为整棵 Fiber 树的根。

    • containerInfo 指向 div#root DOM 容器
    • stateNode 或相关属性标记宿主环境
  3. 构建 Fiber 树

    • React 从根 Fiber 出发,创建 <App /> 的 Fiber 节点
    • 递归构建子组件 Fiber,直到叶子 DOM 节点
  4. Diff & Reconcile

    • React 对比新旧 Fiber 树
    • 决定 DOM 插入、更新、删除操作
  5. Commit 阶段

    • 真正把 <App /> 对应的 DOM 插入到 div#root

🏗 App 是怎样插入的?

关键对象:

  • ReactDOMRoot
  • FiberNode / HostRootFiber
  • containerInfo 指向页面 DOM
  • <App /> 组件本身

流程:

  1. 入口调用 render

    js 复制代码
    ReactDOM.createRoot(root).render(<App />);
  2. React 创建根 Fiber

    • HostRootFiber 节点作为 Fiber 树根
    • containerInfo 指向 div#root
  3. React 启动调度 / 渲染机制

    • <App /> 被作为 root 元素创建 Fiber 节点
    • JSX 子树继续构建 Fiber,直到叶子节点
  4. DOM 操作应用

    • 所有 DOM 操作作用在 containerInfo
  5. 最终渲染完成

    • <App /> 对应的 DOM 挂载到 div#root

⚙ render 函数源码实现

1. ReactDOM.render (传统 API)

js 复制代码
export function render(element, container, callback) {
  if (!isValidContainerLegacy(container)) {
    throw new Error('Target container is not a DOM element.');
  }

  return legacyRenderSubtreeIntoContainer(
    null,
    element,
    container,
    false,
    callback,
  );
}
  • 核心逻辑:调用 legacyRenderSubtreeIntoContainer,把元素渲染到 DOM 容器

2. ReactDOMRoot.render (新 API)

js 复制代码
ReactDOMRoot.prototype.render = function(children) {
  const root = this._internalRoot;
  if (root === null) throw new Error('Cannot update an unmounted root.');
  updateContainer(children, root, null, null);
};
  • 核心:把用户传入的 <App /> 交给 updateContainer 处理
  • _internalRoot 就是 FiberRoot 对象

🔑 updateContainer 的原理

updateContainer 是将用户 JSX 转换为 React 内部更新机制的桥梁。

函数签名

js 复制代码
export function updateContainer(
  element,     // 要渲染的 JSX 元素
  container,   // Fiber 根节点
  parentComponent, 
  callback
) { ... }

工作流程

  1. 获取当前 Fiber / 时间 / 优先级
js 复制代码
const current = container.current;
const eventTime = requestEventTime();
const lane = requestUpdateLane(current);
  • 获取根 Fiber
  • 事件时间戳
  • 确定更新优先级(Lane)
  1. 处理 Context
js 复制代码
const context = getContextForSubtree(parentComponent);
if (!container.context) container.context = context;
else container.pendingContext = context;
  • 为子树传递 Context
  1. 创建 Update 对象
js 复制代码
const update = createUpdate(eventTime, lane);
update.payload = { element };
if (callback) update.callback = callback;
  • 将 JSX 包装为内部 Update 对象

  • 包含:

    • eventTimelanepayloadcallback
  1. 加入更新队列并调度
js 复制代码
const root = enqueueUpdate(current, update, lane);
if (root !== null) {
  scheduleUpdateOnFiber(root, current, lane, eventTime);
  entangleTransitions(root, current, lane);
}
  • enqueueUpdate:加入 Fiber 的更新队列
  • scheduleUpdateOnFiber:开始调度渲染
  • entangleTransitions:处理 Transition 优先级纠缠

🔄 渲染流程总结

js 复制代码
用户调用 root.render(<App />)
         ↓
    updateContainer
         ↓
     enqueueUpdate (加入更新队列)
         ↓
   scheduleUpdateOnFiber (触发调度)
         ↓
    renderRootSync/Concurrent (构建 Fiber 树)
         ↓
     commitRoot (提交到 DOM)

✅ 总结

  1. root.render(<App />) 的本质是:

    • 将用户 JSX 转换成 Fiber 更新对象
    • 挂到根 Fiber 上
    • 启动调度 / 渲染 / commit 流程
  2. updateContainer 是 render 的关键桥梁:

    • 处理 Context
    • 创建 Update
    • 入队并触发调度
  3. Fiber 树最终构建完成后,DOM 被插入 containerInfo 指向的真实容器

一句话概括:

render = JSX → Update → Fiber 更新队列 → 调度 → render → commit → DOM

后序

本篇已经到了scheduleUpdateOnFiber函数进入调和,也就是react-reconciler的工作,下一篇我们将讲解react的render流程,下图来自图解react

相关推荐
北城以北88884 小时前
Vue--Vue基础(二)
前端·javascript·vue.js
ObjectX前端实验室4 小时前
【react18原理探究实践】更新调度的完整流程
前端·react.js
tanxiaomi5 小时前
通过HTML演示JVM的垃圾回收-新生代与老年代
前端·jvm·html
palpitation975 小时前
Android App Links 配置
前端
FuckPatience5 小时前
Vue 组件定义模板,集合v-for生成界面
前端·javascript·vue.js
sophie旭5 小时前
一道面试题,开始性能优化之旅(3)-- DNS查询+TCP(三)
前端·面试·性能优化
开心不就得了5 小时前
构建工具webpack
前端·webpack·rust
gerrgwg5 小时前
Flutter中实现Hero Page Route效果
前端
不枯石6 小时前
Matlab通过GUI实现点云的ICP配准
linux·前端·图像处理·计算机视觉·matlab