|
| 1 | +# 📖 2.JSX转换 - 学习总结 |
| 2 | + |
| 3 | +> **生成时间**: 2025-10-17 02:08:41 |
| 4 | +> **原笔记**: notes-react/notes/2.JSX转换.md |
| 5 | +
|
| 6 | +## 🎯 学习主题 |
| 7 | + |
| 8 | +本次学习的核心主题是 **React JSX 转换机制**,重点深入理解了 JSX 从编译时到运行时的完整转换流程,特别是 React 17+ 新版 JSX 转换规范的实际实现。 |
| 9 | + |
| 10 | +## 🔧 技术要点 |
| 11 | + |
| 12 | +### 1. JSX 转换的双阶段架构 |
| 13 | + |
| 14 | +**编译时(Babel 处理)**: |
| 15 | +- **词法分析**:将 JSX 代码拆分为 Token 流(标签、属性、文本等) |
| 16 | +- **语法分析**:构建抽象语法树(AST),处理嵌套结构和动态表达式 |
| 17 | +- **代码生成**:根据 React 版本生成不同的函数调用格式 |
| 18 | + |
| 19 | +**运行时(React 处理)**: |
| 20 | +- 执行 `jsx()` 或 `React.createElement()` 函数 |
| 21 | +- 创建虚拟 DOM 对象(React Element) |
| 22 | +- 构建虚拟 DOM 树并进行 Diff 计算 |
| 23 | + |
| 24 | +### 2. React 17+ JSX 转换的重大变化 |
| 25 | + |
| 26 | +通过代码实现,深刻理解了新旧版本的差异: |
| 27 | + |
| 28 | +**React 17 前**: |
| 29 | +```javascript |
| 30 | +// 需要手动引入 React |
| 31 | +React.createElement('div', |
| 32 | + null, |
| 33 | + 'hello', |
| 34 | + React.createElement('span', null, 'world') |
| 35 | +) |
| 36 | +``` |
| 37 | + |
| 38 | +**React 17+**: |
| 39 | +```javascript |
| 40 | +// 自动导入 jsx-runtime |
| 41 | +import { jsx as _jsx } from 'react/jsx-runtime'; |
| 42 | +jsx('div', { |
| 43 | + children: ['hello', jsx('span', { children: 'world' })] |
| 44 | +}) |
| 45 | +``` |
| 46 | + |
| 47 | +### 3. 核心代码实现逻辑 |
| 48 | + |
| 49 | +**React Element 工厂函数**: |
| 50 | +```typescript |
| 51 | +const ReactElement = function(type: ElementType, key: Key, ref: Ref, props: Props) { |
| 52 | + return { |
| 53 | + $$typeof: REACT_ELEMENT_TYPE, |
| 54 | + type, // 元素类型 |
| 55 | + key, // 元素 key |
| 56 | + ref, // 元素引用 |
| 57 | + props, // 元素属性 |
| 58 | + _mark: '_juanlou' // 调试标记 |
| 59 | + }; |
| 60 | +}; |
| 61 | +``` |
| 62 | + |
| 63 | +**JSX 函数的关键改进**: |
| 64 | +- **参数简化**:从 `jsx(type, config, ...maybeChildren)` 改为 `jsx(type, config)` |
| 65 | +- **子元素处理**:移除了手动处理 children 的逻辑,改为通过 config.children 传递 |
| 66 | +- **属性提取**:统一从 config 对象中提取 key、ref 和其他 props |
| 67 | + |
| 68 | +### 4. 模块化打包架构 |
| 69 | + |
| 70 | +通过 Rollup 配置实现了完整的模块打包: |
| 71 | +- **UMD 格式**:支持浏览器和 Node.js 环境 |
| 72 | +- **自动生成 package.json**:根据源码包信息动态生成 |
| 73 | +- **多入口打包**:分别打包 `react`、`jsx-runtime`、`jsx-dev-runtime` |
| 74 | + |
| 75 | +## 💡 深度理解 |
| 76 | + |
| 77 | +### 1. JSX 转换的设计哲学演进 |
| 78 | + |
| 79 | +通过实现过程,深刻理解了 React 团队为什么要引入新的 JSX 转换: |
| 80 | + |
| 81 | +**Tree-shaking 优化**:新版将 JSX 运行时分离到独立包,未使用的 JSX 代码可以被正确摇树优化。 |
| 82 | + |
| 83 | +**简化开发体验**:不再需要手动引入 React,减少了开发者的心智负担。 |
| 84 | + |
| 85 | +**性能优化**:新的 children 传递方式(通过 config.children)为后续的优化提供了更好的基础。 |
| 86 | + |
| 87 | +### 2. 虚拟 DOM 创建的精妙设计 |
| 88 | + |
| 89 | +在实现 `jsx()` 函数时,发现了几个关键的设计细节: |
| 90 | + |
| 91 | +**安全属性复制**: |
| 92 | +```typescript |
| 93 | +// 只复制自身属性,防止原型链污染 |
| 94 | +if (hasOwnProperty.call(config, prop)) { |
| 95 | + props[prop] = val; |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +**Symbol 标识的重要性**: |
| 100 | +```typescript |
| 101 | +$$typeof: REACT_ELEMENT_TYPE // Symbol.for('react.element') |
| 102 | +``` |
| 103 | +这个设计是为了防止 XSS 攻击,确保只有 React 创建的对象才能被识别为合法的 React Element。 |
| 104 | + |
| 105 | +### 3. 在 React 整体架构中的位置 |
| 106 | + |
| 107 | +通过这次实现,明确了 JSX 转换在 React 架构中的关键作用: |
| 108 | + |
| 109 | +``` |
| 110 | +JSX 语法 |
| 111 | + ↓ (Babel 编译时) |
| 112 | +jsx() 函数调用 |
| 113 | + ↓ (运行时 - react 包) |
| 114 | +React Element 对象 |
| 115 | + ↓ (协调阶段 - react-reconciler) |
| 116 | +Fiber 节点 |
| 117 | + ↓ (渲染阶段 - react-dom) |
| 118 | +真实 DOM |
| 119 | +``` |
| 120 | + |
| 121 | +**承上启下的桥梁**:JSX 转换是连接开发者编写的声明式代码与 React 内部 imperative 渲染逻辑的关键桥梁。 |
| 122 | + |
| 123 | +### 4. 实际开发中的应用价值 |
| 124 | + |
| 125 | +**调试能力提升**:通过自定义 `_mark: '_juanlou'` 字段,可以在开发过程中快速识别自定义 React 实现创建的元素。 |
| 126 | + |
| 127 | +**理解 Babel 配置**:现在能准确理解为什么项目中需要配置 `@babel/preset-react` 的 `runtime` 选项。 |
| 128 | + |
| 129 | +**性能优化意识**:明白了为什么 React 团队要改变 children 的传递方式 - 为未来的编译器优化做准备。 |
| 130 | + |
| 131 | +这次实践不仅实现了功能,更重要的是通过代码变更理解了 React 团队的设计决策背后的深层原因,为后续学习 Fiber 架构和协调算法打下了坚实的基础。 |
| 132 | + |
| 133 | +--- |
| 134 | +*🤖 由 AI Commit Note 自动生成* |
0 commit comments