React JSX 转换原理与 GSR 实现解析
本文是我在学习 React JSX 转换原理及其底层实现机制时的笔记与理解,主要涉及 GSR(即自定义的
createElement实现)、Babel 编译机制、React 包结构及 JSX 的运行逻辑。通过梳理 JSX → JS → DOM 的全过程,深入理解 React 声明式编程的本质。
一、React 项目结构与包关系
在 React 的源码架构中,各个包之间分工明确:
- react包
 提供与运行环境无关的通用方法和数据结构(例如 ReactElement 的定义与创建)。
 可以理解为 React 的"核心逻辑层"。
- react-reconciler包
 负责协调(reconcile)虚拟 DOM 树与真实 DOM 的差异。
 这是 React 更新机制的核心部分。
- react-dom包
 作为浏览器环境的实现层,将协调结果映射到实际的 DOM 操作上。
- shared包
 定义了通用类型与工具函数,供多个包复用。
这种拆分结构的优势在于:React 可以被用于浏览器、Native、甚至自定义渲染器中,而核心逻辑只需要维护一份。
二、JSX 是什么?为什么是 "XML" 语法糖?
1. JSX 并不是 HTML
很多初学者容易误会 JSX 是 HTML。
其实,JSX 只是 JavaScript 的一种语法扩展,让我们可以用更直观的方式声明 UI 结构。
例如:
            
            
              ini
              
              
            
          
          const element = <h1 className="title">Hello React</h1>;在编译后,会被 Babel 转换为:
            
            
              ini
              
              
            
          
          const element = React.createElement("h1", { className: "title" }, "Hello React");所以,JSX 最终都会变成 JavaScript 函数调用。
2. 为什么说是 "XML-like"?
JSX 采用了与 XML 类似的标签结构(如 <tag attr="value">children</tag>),语法上接近 HTML,但并不等价于 HTML。
例如:
- JSX 中的 class必须写成className;
- JSX 中可以嵌入任意 JS 表达式 {};
- JSX 语义最终由 JS 决定,而非浏览器 DOM。
换句话说,JSX 是一种 "声明界面结构的 XML 语法糖" ,
它让我们可以像写 HTML 一样"声明"界面,但底层执行的仍是 JS 逻辑。
3. "语法糖"是什么意思?
"语法糖"(Syntactic Sugar)指的是让代码更易读、更易写的语法形式,但不会改变语言本身的功能。
举个例子:
            
            
              css
              
              
            
          
          <div>Hello</div>和
            
            
              csharp
              
              
            
          
          React.createElement("div", null, "Hello")两者最终效果一致,只是前者更"甜"一点(更直观),因此叫"语法糖"。
三、JSX 转换原理:编译时与运行时
JSX 的实现过程分为两个阶段:
1. 编译时(Babel 转换)
由 Babel 完成,将 JSX 转换为 JS 函数调用。
转换规则示例:
            
            
              css
              
              
            
          
          <Button color="blue">Click</Button>➡️ 编译后变为:
            
            
              css
              
              
            
          
          React.createElement(Button, { color: "blue" }, "Click");Babel 在这里扮演"语法翻译器"的角色。
2. 运行时(执行 GSR 或 React.createElement)
编译后的代码会调用运行时提供的 React.createElement 方法。
在自定义实现(如 GSR 转换)中,我们可以自定义该方法的行为。
示例实现:
            
            
              csharp
              
              
            
          
          function GSR(type, config, ...children) {
  const props = { ...config };
  if (children.length > 0) {
    props.children = children.length === 1 ? children[0] : children;
  }
  return { type, key: props.key || null, ref: props.ref || null, props };
}最终返回一个 ReactElement 对象:
            
            
              yaml
              
              
            
          
          {
  type: 'div',
  key: null,
  ref: null,
  props: { className: 'app', children: 'Hello React' }
}这就是虚拟 DOM(Virtual DOM)的基本形态。
四、RedElement 与 RedSymbol 的设计
在 React 内部,虚拟 DOM(ReactElement)是以对象结构存在的。
项目中自定义实现的 RedElement 结构如下:
            
            
              rust
              
              
            
          
          {
  $$typeof: Symbol('red.element'),
  type,
  key,
  ref,
  props
}- $$typeof:用于区分对象类型,防止滥用;
- type:元素类型(如- 'div'或组件);
- key:用于 Diff;
- ref:用于访问 DOM 或组件;
- props:组件属性。
为了确保每个元素的唯一性,引入了 RedSymbol:
- 如果环境支持 Symbol,就使用Symbol('red.element');
- 否则使用一个数字常量代替。
这样可以保证每个 ReactElement 在调试和识别时都是安全且唯一的。
五、React.createElement 的底层逻辑
React 的 createElement 实现与 GSR 几乎一致。
伪代码如下:
            
            
              ini
              
              
            
          
          function createElement(type, config, ...children) {
  const props = {};
  let key = null;
  let ref = null;
  if (config) {
    key = config.key ?? null;
    ref = config.ref ?? null;
    for (const prop in config) {
      if (prop !== 'key' && prop !== 'ref') {
        props[prop] = config[prop];
      }
    }
  }
  if (children.length > 0) {
    props.children = children.length === 1 ? children[0] : children;
  }
  return ReactElement(type, key, ref, props);
}这一过程说明:
- JSX 的"声明式"结构只是表象;
- React 内部依然是命令式 JS 函数调用;
- React 在运行时会再通过 reconciler 将这些虚拟节点映射为真实 DOM 操作。
六、React 17 之后 JSX 转换变化
React 17 之后引入了新的 JSX 转换方案。
即不再强制要求显式导入 React:
            
            
              javascript
              
              
            
          
          // React 17 之前
import React from 'react';
const element = <div />;
// React 17 之后
const element = <div />; // 仍可正常工作Babel 会自动引入新的运行时辅助函数 jsx 或 jsxs,例如:
            
            
              javascript
              
              
            
          
          import { jsx as _jsx } from 'react/jsx-runtime';
_jsx("div", { className: "box" });这使得编译输出更简洁、性能更高。
七、总结与理解提升
- JSX 是一种 声明式的语法糖,让我们以接近 HTML 的方式描述组件结构;
- 它本质是 JavaScript 调用 React.createElement 的语法封装;
- 编译阶段由 Babel 转换,运行阶段由 React 运行时(或自定义 GSR)解析;
- React 底层依然是命令式的,通过协调器将虚拟 DOM 同步到真实 DOM;
- React 17 之后引入了新的 JSX 转换机制,使开发体验更自然。
结语
理解 JSX 转换原理,就能更清楚地认识到 React 的核心哲学:
"表面是声明式的 JSX,实质是命令式的 JavaScript。"
这也是 React 高效、灵活的根本原因------它让开发者用声明的方式思考,但底层通过命令式逻辑精确地控制 DOM。