第 1 周:React 源码结构 + 构建系统

🎯 目标:
  • 理解 monorepo 结构

  • 看懂 React.createElement 和 JSX 转换

  • 明白 React 是如何通过 Rollup 构建的

📂 推荐文件:
  • /packages/react/src/ReactElement.js

  • /scripts/rollup/* 构建脚本

  • /packages/shared/* 公共方法

如何看懂 React.createElement 和 JSX 转换

一、核心概念:JSX 是 React.createElement 的语法糖

比如这段 JSX:

const element = <h1>Hello, world!</h1>;

React 编译时会转译为:

const element = React.createElement('h1', null, 'Hello, world!');

这就是 JSX 本质上做的事情。

二、React.createElement 的结构

React.createElement(type, props, ...children) 接受三个参数:

  1. type: 要创建的元素类型,字符串如 'div' 或组件名如 MyComponent

  2. props: 属性对象,传给元素的属性

  3. ...children: 子节点,可以是文本、React 元素、数组等

比如:

const element = <div id="app">Hello</div>;

等价于:

const element = React.createElement('div', { id: 'app' }, 'Hello');

三、多个子元素的情况

const element = (
  <ul>
    <li>One</li>
    <li>Two</li>
  </ul>
);

转成:

const element = React.createElement(
  'ul',
  null,
  React.createElement('li', null, 'One'),
  React.createElement('li', null, 'Two')
);

children 参数支持多个,也就是使用剩余参数 ...children 的形式。

四、JSX 转换方式演示

你可以使用 babel 在线工具 查看 JSX 是如何被转换成 React.createElement 的:

  1. 打开 https://babeljs.io/repl

  2. 选择 @babel/preset-react

  3. 输入 JSX

  4. 右侧即可看到转换后的 JS 代码

五、使用 React 17+ 或 React 18 的 JSX 转换(新版)

从 React 17 开始,React 引入了新的 JSX 转换机制,使用 jsx 函数而不是 React.createElement。但概念是一样的,只是底层实现方式优化了。

import { jsx } from 'react/jsx-runtime';

 比如:

const element = <div>Hello</div>;

转译后是:

import { jsx as _jsx } from "react/jsx-runtime";
const element = _jsx("div", { children: "Hello" });

总结重点记住:

JSX等价 React.createElement
<div>Hello</div>React.createElement('div', null, 'Hello')
<div id="app" />React.createElement('div', { id: 'app' })
<Comp title="Hi" />React.createElement(Comp, { title: 'Hi' })
<ul><li>1</li><li>2</li></ul>嵌套多个 React.createElement

理解react中使用的 monorepo 结构

什么是 Monorepo?

  • Monorepo(单一代码仓库)就是:
    一个 Git 仓库里管理多个包(package)或模块(module)

跟它对立的是 多仓库(Polyrepo),每个模块单独一个 Git 仓库。

在 Monorepo 里,通常每个模块都有自己独立的 package.json,可以独立发布、独立构建,但又能共享代码、统一管理。

React 为什么要用 Monorepo?

React 官方(facebook/react 仓库)用了 Monorepo,主要原因有:

  1. 模块非常多,例如:

    • react

    • react-dom

    • scheduler

    • react-reconciler

    • react-test-renderer

    • react-refresh

    • react-devtools

    • ...

  2. 模块之间强依赖,需要同步开发
    比如 reactreact-dom 有非常紧密的关系,如果分多个仓库很难同步管理。

  3. 代码共享和版本管理统一

    • 统一构建脚本(build scripts)

    • 统一发布流程

    • 不容易版本错乱

  4. 方便大规模协作
    跨团队开发不同子模块,但依然能统一在一个仓库内 CI/CD。

React 的 Monorepo 结构长什么样?

react/
├── packages/          # 所有子模块
│   ├── react/          # React 核心包
│   ├── react-dom/      # React DOM 渲染相关
│   ├── scheduler/      # 调度器
│   ├── shared/         # 公共代码
│   ├── react-reconciler/ # Diff 算法相关
│   └── ...
├── scripts/           # 构建和开发用的脚本
├── fixtures/          # 测试用例和 demo
├── build/             # 构建产物
├── .babelrc           # Babel 配置
├── package.json       # 顶层的包管理
└── yarn.lock

 

核心目录是 packages/,里面每个文件夹就是一个独立的 npm 包。

比如:

  • packages/react/package.json

  • packages/react-dom/package.json

  • packages/scheduler/package.json

每个子包可以单独构建、测试、发布!

Monorepo 工具

React 官方没有用 Lerna,但是很多 Monorepo 项目用这些工具来管理:

  • Lerna:老牌 Monorepo 工具,管理版本、发布。

  • Turborepo:Vercel 出品的现代 Monorepo 构建工具。

  • Nx:大型项目专用,提供模块依赖分析、增量构建等。

这些工具可以帮你在 Monorepo 中:

  • 更快地构建

  • 更高效地运行测试

  • 更方便地管理子包版本

明白 React 是如何通过 Rollup 构建的 

1. 为什么 React 要用 Rollup?

  • React 是库,不是应用,所以需要构建成 多种格式(比如 CommonJS、ESM、UMD 等),适配各种使用场景。

  • Rollup 是专门为打包「库」设计的,输出体积小,tree-shaking 友好,非常适合 React 这种基础库。

  • 相比 Webpack,Rollup 构建出来的包更加干净、轻量。

所以,React 团队选了 Rollup 来打包源代码。

2. React 的源码结构是怎样的?参考前一个模块

React 源码并不是单一的一个文件,而是一个 monorepo(多包仓库)

  • 每个功能模块(比如 reactreact-dom)是一个独立的 package。

  • scripts/rollup/ 是专门写 Rollup 配置和打包逻辑的地方。

  • 最重要的文件是:

    • bundles.js —— 定义了要打哪些包(比如 react.production.min.jsreact.development.js)。

    • rollup.config.js —— 定义了 Rollup 配置的生成逻辑。

3. React 的 Rollup 构建流程

阶段详细内容
Step 1执行构建脚本,比如 yarn build react
Step 2读取 bundles.js,根据里面的配置生成多个 bundle(产物列表)
Step 3每个 bundle 根据 rollup.config.js 动态生成对应的 Rollup 配置(entry、output、plugins 等)
Step 4Rollup 根据配置文件打包,输出各种格式的产物(比如 CJS、ESM、UMD)
Step 5特别处理 development 和 production 两种环境(比如开发版保留警告信息,生产版去除警告并压缩)

4. 深入细节:bundles.js 是什么?

简单来说,bundles.js 里面定义了:

  • 打哪些入口文件(entry)

  • 输出成什么名字(比如 react.development.js

  • 输出格式(如 CJS、UMD、ESM)

  • 是否要区分开发版和生产版

举个小例子👇:

const ReactBundle = {
  label: 'react',
  bundleTypes: [
    BUNDLE_TYPES.DEV,
    BUNDLE_TYPES.PROD,
    BUNDLE_TYPES.UMD_DEV,
    BUNDLE_TYPES.UMD_PROD,
    BUNDLE_TYPES.NODE_ESM,
  ],
  entry: 'react',
};

module.exports = [ReactBundle];

 

就是说:

  • entry: 'react' 表示打包 packages/react/src/index.js

  • 打各种不同环境的版本(开发版、生产版、Node ESM版等等)

5. 深入细节:rollup.config.js 是什么?

这个文件是根据 bundles.js 生成最终 Rollup 配置的地方。

核心逻辑:

  • 遍历 bundles.js 定义的 bundle

  • 为每个 bundle 设置 entry、output、plugins

  • 插入自定义的 Rollup 插件,比如:

    • 替换开发/生产环境变量(比如 process.env.NODE_ENV

    • 处理 Flow 类型注释

    • 处理警告信息

    • 最后压缩 production 版本(Terser)

React 自己还维护了一套 专用插件,比如 replace-process-envstrip-dev-code 之类。


6. React 会输出哪些格式?

React 打包出来的格式有好几种,分别对应不同的使用场景:

产物说明用途
CommonJS (cjs)Node 环境 / 老的 bundlerrequire('react')
UMD (umd)浏览器直接用 <script> 引入<script src="react.development.js">
ESM (esm)现代前端项目import React from 'react'
Facebook 内部专用版本内部平台使用

 

7. 总结一句话

React 用 Rollup 根据 bundles.js 配置,动态生成多份构建产物(CJS/UMD/ESM),区分开发/生产环境,并且通过定制的 Rollup 插件实现代码替换、压缩等操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值