Skip to content
字数
1681 字
阅读时间
7 分钟

React 应用从启动到渲染成页面的过程涉及一套完整的流程,涉及初始化、编译、协调(Diff)、提交等多个阶段,核心目标是将 JSX 描述的组件树高效转换为真实 DOM 并渲染到页面。以下是详细流程拆解:

  1. 编译准备:JSX 被编译为 createElement 调用,入口文件定义根组件和挂载点。
  2. 初始化启动:调用 root.render(),创建 Fiber 根节点,触发根组件渲染,生成虚拟 DOM 树。
  3. 协调阶段:首次渲染时全量创建 Fiber 节点(更新时执行 Diff 找差异),通过 Fiber 调度拆分任务,避免主线程阻塞。
  4. 提交阶段:执行 DOM 插入/修改/删除操作,触发生命周期/副作用,最终浏览器渲染页面。

一、启动前的准备:代码编译与环境初始化

在应用真正运行前,需要先完成代码转换和环境配置,为运行时渲染做准备。

  1. JSX 编译为虚拟 DOM 函数
    React 应用中编写的 JSX(如 <div><span>Hello</span></div>)并非原生 JavaScript,需要通过 Babel 等编译器 转换为 React.createElement 调用(或 React 17+ 的 jsx 函数)。
    示例:

    jsx
    // 源代码
    <div className="app">Hello</div>
    // 编译后(简化)
    React.createElement('div', { className: 'app' }, 'Hello')

    该函数执行后会生成 虚拟 DOM 对象(描述节点类型、属性、子节点的 JavaScript 对象),避免直接操作真实 DOM 的性能开销。

  2. 入口文件与根组件定义
    应用通过入口文件(通常是 index.js)指定根组件和挂载点,例如:

    javascript
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import App from './App';
    
    // 获取页面中的根容器(真实 DOM 节点)
    const root = ReactDOM.createRoot(document.getElementById('root'));
    // 挂载根组件 App
    root.render(<App />);

二、启动阶段:初始化与首次渲染触发

root.render(<App />) 执行时,React 正式启动渲染流程,核心是将根组件转换为可渲染的虚拟 DOM 树。

  1. 创建 Fiber 根节点
    React 会初始化一个 Fiber 根节点FiberRoot),作为整个应用的“调度中心”。Fiber 是 React 16+ 引入的工作单元,每个节点对应一个组件或 DOM 元素,存储了节点类型、属性、子节点指针等信息,用于后续的任务拆分和调度。

  2. 根组件渲染(生成虚拟 DOM 树)
    React 会执行根组件 App 的渲染逻辑(函数组件执行函数体,类组件执行 render 方法),递归渲染其所有子组件,最终生成一棵完整的 虚拟 DOM 树(以 Fiber 节点链表的形式存储)。
    例如,App 组件若包含 HeaderContent 子组件,会依次渲染这两个组件,拼接成完整的虚拟 DOM 结构。

三、协调阶段(Reconciliation):Diff 算法与任务调度

协调阶段是 React 性能优化的核心,目标是确定虚拟 DOM 树中需要更新的部分,且该过程可被中断(由 Fiber 架构支持)。

  1. 首次渲染的“协调”:全量创建 Fiber 节点
    首次渲染时,由于没有旧的虚拟 DOM 树可供对比,React 会直接根据新虚拟 DOM 树 创建所有 Fiber 节点,并标记为“需要插入到真实 DOM”的状态。
    (注:后续更新时,此阶段才会执行 Diff 算法对比新旧 Fiber 树,找出差异)

  2. Fiber 调度(Scheduler)
    React 通过 调度器(Scheduler) 管理协调阶段的任务:

    • 将 Fiber 树的创建/对比拆分为多个小任务(每个任务处理一个 Fiber 节点);
    • 每次只执行一个小任务(约 5ms),执行完后检查浏览器是否有空闲时间(通过 requestIdleCallback 模拟);
    • 若有高优先级任务(如用户输入、动画),则暂停当前任务,优先处理高优先级任务,避免页面卡顿。

四、提交阶段(Commit):真实 DOM 操作与页面渲染

协调阶段完成后,React 进入不可中断的提交阶段,将协调阶段计算出的差异应用到真实 DOM,并触发生命周期/副作用。

  1. 执行 DOM 操作
    React 遍历 Fiber 树中标记为“需要更新”的节点(首次渲染时所有节点均需插入),执行对应的 DOM 操作:

    • 新增节点:调用 document.createElement 创建真实 DOM 元素,设置属性(classNamestyle 等),并插入到父节点中;
    • (更新时)修改节点:只更新变化的属性(如 onClick 事件、src 等),避免全量重设;
    • (更新时)删除节点:调用 removeChild 移除不再需要的真实 DOM 元素。
  2. 触发生命周期与副作用

    • 类组件:触发 componentDidMount(首次渲染)、componentDidUpdate(更新)等生命周期方法;
    • 函数组件:执行 useEffectuseLayoutEffect 等 Hook 的副作用(useLayoutEffect 在 DOM 操作后同步执行,useEffect 在浏览器绘制后异步执行)。
  3. 完成渲染,页面可见
    当所有 DOM 操作执行完毕,浏览器会触发重排(Reflow)和重绘(Repaint),将更新后的 DOM 树渲染到页面上,用户即可看到最终的界面。

五、总结:从启动到渲染的完整流程

  1. 编译准备:JSX 被编译为 createElement 调用,入口文件定义根组件和挂载点。
  2. 初始化启动:调用 root.render(),创建 Fiber 根节点,触发根组件渲染,生成虚拟 DOM 树。
  3. 协调阶段:首次渲染时全量创建 Fiber 节点(更新时执行 Diff 找差异),通过 Fiber 调度拆分任务,避免主线程阻塞。
  4. 提交阶段:执行 DOM 插入/修改/删除操作,触发生命周期/副作用,最终浏览器渲染页面。

整个流程中,Fiber 架构的任务拆分、调度器的优先级管理,以及 Diff 算法的高效对比,共同保证了 React 应用在复杂场景下的渲染性能和用户体验。

贡献者

The avatar of contributor named as chenjie chenjie

页面历史

撰写