🔥🔥🔥 React18 源码学习 - 容器的挂载

前言

本文的React代码版本为18.2.0

可调试的代码仓库为:GitHub - yyyao-hh/react-debug at master-pure

当我们初次学习React时,第一个接触的API往往是ReactDOM.render。这个方法是React应用与浏览器 DOM连接的桥梁,也是整个应用渲染的起点。理解它的实现原理能为后续理解组件生命周期、虚拟DOM和协调算法打下坚实基础。

ReactDOM.render 的基本调用

React应用中,我们通常这样使用ReactDOM.render

  • React17及之前:首先获取一个DOM容器,然后将应用进行挂载。
javascript 复制代码
/* src/index.js */

import ReactDOM from 'react-dom';
import App from './App';

const root = document.getElementById('root');
ReactDOM.render(<App />, root);
  • React18开始:首先需要创建一个能够支持并发特性的root对象,然后将应用进行挂载。
javascript 复制代码
/* src/index.js */

import * as ReactDOM from 'react-dom/client';
import App from './App';

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

ReactDOM.createRoot的作用便是创建React根节点:作为一个长期存在的、用于管理该容器内整个React树的控制中心。接下来将围绕这个函数进行深入分析!

ReactDOM.renderReact18中已被标记为"废弃"(legacy)。它创建的根节点运行在遗留模式(Legacy Mode)下,其渲染过程是同步、不可中断的。

ReactDOM.createRoot创建的根节点,则默认启用了并发模式(Concurrent Mode)。它允许React在准备渲染更新时进行中断、暂停和恢复,将主线程的控制权交还给浏览器以优先处理高优先级的用户交互(如输入、动画),从而带来更流畅的用户体验。

ReactDOM.createRoot:构建根容器

我们现在开始深入了解下ReactDOM.createRoot这个函数:

javascript 复制代码
/* src/index.js */

import ReactDOM from 'react-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));

在深入阅读源码后我们会了解到,ReactDOM.createRoot通过层层调用最终停在了某个createRoot方法中,这时的createRoot主要做了三件事:

  1. createContainer: 创建了根容器对象FiberRoot
  2. listenToAllSupportedEvents: 代理了所有事件,这是React合成事件系统的基石
  3. new ReactDOMRoot: 创建ReactDOMRoot实例并返回。该实例对外暴露了renderunmount方法
scss 复制代码
/* src/react/packages/react-dom/client.js */
export function createRoot(...): RootType {
  return createRootImpl(...);
}

/* src/react/packages/react-dom/src/client/ReactDOM.js */
function createRoot(...): RootType {
  return createRootImpl(...);
}

/* src/react/packages/react-dom/src/client/ReactDOMRoot.js */
export function createRoot(...): RootType {
  // 1. 创建容器对象 FiberRoot
  const root = createContainer(...);

  // 2. 代理所有事件
  listenToAllSupportedEvents(rootContainerElement);

  // 3. 返回公开的根对象
  return new ReactDOMRoot(root);
}

然后我们看到createContainer函数中又继续调用了createFiberRoot函数。在这个函数的内部依旧做了三件比较关键的事:

  1. 创建 FiberRootNode
  2. 创建 RootFiberNode
  3. 进行关联,二者通过.current.stateNode互相引用,形成闭环:
arduino 复制代码
/* src/react/packages/react-reconciler/src/ReactFiberReconciler.old.js */
export function createContainer(...): OpaqueRoot {
  return createFiberRoot(...);
}

/* src/react/packages/react-reconciler/src/ReactFiberRoot.old.js */
export function createFiberRoot(...): FiberRoot {
  // 1. 创建 FiberRoot
  const root: FiberRoot = (new FiberRootNode(...));

  // 2. 创建 RootFiber
  const uninitializedFiber = createHostRootFiber(...);
  
  // 3. 将二者进行关联
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;

  return root;
}

看到这里大家肯定对这新出现的结构一脸懵 o((⊙﹏⊙))o

  • FiberRootNodeFiber树的容器,它代表了整个React应用的根节点,与实际的DOM容器(例如div#root)相关联。每个React应用通常只有一个FiberRoot
  • RootFiberNodeFiber树的根节点,即HostRoot。它是Fiber树结构的起点,其子节点就是应用顶层组件(例如<App />

最后顺便看一下用于生成RootFiber的函数:createHostRootFiber。它通过调用函数createFiber并传入HostRoot参数,生成了一个代表根节点的Fiber

这里还有很多Fiber类型,会在Fiber相关的章节详细讲到

javascript 复制代码
/* src/react/packages/react-reconciler/src/ReactWorkTags.js */
export const FunctionComponent = 0; // 函数组件
export const ClassComponent = 1;    // 类组件
export const HostRoot = 3;          // 根节点

/* src/react/packages/react-reconciler/src/ReactFiber.old.js */
export function createHostRootFiber(...): Fiber {
  return createFiber(HostRoot, null, null, mode);
}

const createFiber = function(...): Fiber {
  return new FiberNode(...);
}

总结

本文从宏观角度解析了ReactDOM.createRoot如何为React应用初始化容器、创建核心数据结构,并最终准备好渲染的舞台。理解这一过程是掌握React并发渲染模型(Concurrent Mode)的基础。

下一章我们将学习另一个重要的数据结构:Fiber

相关推荐
chao_66666619 分钟前
React Native + Expo 开发指南:编译、调试、构建全解析
javascript·react native·react.js
码丁_1171 小时前
某it培训机构前端三阶段react及新增面试题
前端·react.js·前端框架
_pengliang1 小时前
react native ios 2个modal第二个不显示
javascript·react native·react.js
我算哪枝小绿植2 小时前
react实现日历拖拽效果
前端·react.js·前端框架
OEC小胖胖2 小时前
04|从 Lane 位图到 `getNextLanes`:React 更新优先级与纠缠(Entangle)模型
前端·react.js·前端框架
愤怒的可乐2 小时前
从零构建大模型智能体:ReAct 智能体实战
前端·react.js·前端框架
BlackWolfSky2 小时前
React中文网课程笔记4—常用工具配置
前端·笔记·react.js
巾帼前端2 小时前
前端框架 React 的虚拟 DOM是如何在这一层层抽象中定位自己位置的?
前端·react.js·前端框架
wayne2142 小时前
React Native 0.80 学习参考:一个完整可运行的实战项目
学习·react native·react.js
光影少年2 小时前
你在 React 里具体做过哪些性能优化?
前端·react.js·性能优化