人人都会AI编程

2.4 元素渲染与 DOM 挂载机制

更新时间:2026-07-10

React 应用启动时,需要将组件树描述的 UI 变成浏览器中真实可见的像素。这个过程可以拆解为两个关键动作:创建 React 元素(虚拟 DOM 节点)将元素挂载到页面。理解这个机制,能帮你更好地把握 React 应用的启动流程和更新本质。

React 元素的本质:一个普通的 JavaScript 对象

你在 JSX 中书写的每个标签,都会被编译成 React.createElement 调用,最终产出一个 React 元素对象。这个对象是对真实 DOM 的轻量描述,也就是我们常说的”虚拟 DOM 节点”。

// 编写的 JSX
const element = <h1 className="title">Hello, World!</h1>;

编译后的结果大致等价于:

const element = React.createElement(
  'h1',
  { className: 'title' },
  'Hello, World!'
);

element 的实际结构是一个扁平的 JavaScript 对象:

{
  type: 'h1',
  key: null,
  ref: null,
  props: {
    className: 'title',
    children: 'Hello, World!'
  }
}

这个对象本身并不会渲染任何内容,它只是 视图的描述。只有当这个描述被 React 的渲染器处理时,才会变成真正的 DOM 节点。

从描述到真实 DOM:ReactDOM 的工作

React 核心库(react)负责定义组件和创建元素,但不负责与浏览器打交道。将 React 元素转换为真实 DOM 的职责属于渲染器 react-dom。在 Web 应用中,你必须在入口文件引入 ReactDOM

基本挂载流程:

  1. 在 HTML 中准备一个挂载点(通常是一个 id 为 root<div>)。
  2. 使用 ReactDOM.createRoot 创建一个根节点(React 18 起的新 API)。
  3. 调用 root.render 方法,传入要渲染的 React 元素。
<!-- public/index.html -->
<body>
  <div id="root"></div>
</body>
// src/main.jsx(Vite 项目) 或 index.js(CRA 项目)
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

挂载的具体过程

root.render(<App />) 执行时,React 内部经历了以下几个阶段:

  1. 创建 root Fiber 节点createRoot 会生成一个代表整个应用根节点的 Fiber 节点,并关联到真实 DOM 的容器元素上。Fiber 是 React 内部的协调单元,后续所有更新都以此为基础。
  1. 构造虚拟 DOM 树<App /> 被递归展开为完整的 React 元素树。每个组件函数都会被调用,直到所有子元素都变成不可再拆分的普通 DOM 元素(如 <div><span>)。
  1. 协调(Reconciliation):由于是首次渲染,内存中还不存在旧的 Fiber 树,React 会认为所有元素都是新增,直接生成对应的 DOM 节点。
  1. 提交(Commit):将所有新创建的 DOM 节点一次性插入到 root 容器中,浏览器完成布局和绘制。这个阶段是同步不可中断的,以确保界面一致性。

上述步骤完成后,你就能在页面上看到 App 组件渲染出的内容了。

更新渲染:从首次挂载到后续变化

首次挂载完成后,后续组件的状态变化不会再调用 root.render(通常你只在整个应用入口调用一次)。当内部状态通过 useState 等 Hooks 更新时,React 会自动触发一次 重新渲染

  • 状态变动会标记对应的 Fiber 节点为“需要更新”。
  • React 的调度系统会安排一个更新任务,重新执行该组件的函数,生成新的 React 元素树。
  • 新的元素树与内存中上次渲染时保留的 Fiber 树进行 Diff 比较,找出需要变更的最小 DOM 操作集合。
  • 这些 DOM 变更被打包批量应用到页面上,完成更新。

整个过程对开发者完全透明,你只需要保持状态正确,React 负责高效地更新界面。

常见问题:多次调用 root.render 是否可行?

虽然技术上可以多次调用 root.render 来替换整个应用,但这不是常规的更新方式,会丢失组件内部状态并带来不必要的性能开销。

// ⚠️ 不推荐的“全量刷新”方式
function tick() {
  root.render(
    <h1>当前时间:{new Date().toLocaleTimeString()}</h1>
  );
}
setInterval(tick, 1000);

更符合 React 理念的做法是在组件内部使用 useStatesetInterval,让 React 管理局部更新,而不是从外部暴力替换整棵树。

小结

  • React 元素是对 UI 的不可变描述对象,类似于“设计图纸”。
  • ReactDOM 负责将图纸翻译成真实的 DOM 节点并挂载到页面容器。
  • 挂载过程经历 Fiber 树构建、协调、提交三个阶段,首次渲染后状态更新会触发增量重渲染。
  • 整个应用只需在入口调用一次 createRootrender,后续更新由 React 内部管理。

这一节理解透彻之后,对 React 的生命周期、Hooks 工作时机以及性能优化会有更直观的把握。