前言
你有没有好奇过,为什么在 React 中可以这样写代码:
const element = <h1>Hello, world! 🌍</h1>;
这看起来像是 HTML,但却是写在 .js 文件里的?😱
难道浏览器已经原生支持 JSX 了吗?🤔
答案是:不! ❌
浏览器根本看不懂 JSX,真正“翻译”它的是——Babel 🛠️
今天,我们就来揭开 JSX 的神秘面纱,看看它是如何通过 Babel 转换成真正的 JavaScript 的。🔍
🧩 什么是 JSX?
JSX(JavaScript XML)是一种语法扩展,允许我们在 JavaScript 中编写类似 HTML 的代码。它不是 HTML,也不是字符串,而是一种语法糖🍬。
👉 举个例子:
const greet = <div className="user">Welcome, <span>Qwen</span>! 👋</div>;
这段代码看起来很像 HTML,但其实它最终会被转换成 JavaScript 函数调用。
🔁 JSX 到底发生了什么?Babel 的“翻译”之旅 🚀
Babel 是一个 JavaScript 编译器,它的任务是把新语法(如 JSX、ES6+)转换成浏览器能理解的老版本 JavaScript。
当你写下 JSX 时,Babel 会通过 @babel/preset-react 插件,将 JSX 转换成 React.createElement() 的调用。🛠️
✅ 示例:JSX → JavaScript
我们来看一个简单的例子:
// 你写的 JSX
const element = (
<h1 className="title">Hello, JSX! 🎉</h1>
);
Babel 会把它“翻译”成:
// Babel 输出的 JavaScript
const element = React.createElement(
"h1",
{ className: "title" },
"Hello, JSX! 🎉"
);
是不是很神奇?✨
🧱 React.createElement() 到底做了什么?
React.createElement() 是 React 的核心 API 之一,它负责创建一个 虚拟 DOM 对象(Virtual DOM)。
它的函数签名是:
React.createElement(type, props, ...children)
type:元素类型,比如'div'、'h1'或自定义组件MyComponentprops:属性对象,如{ className: "title" }children:子元素,可以是字符串、数字或其他 React 元素
🔍 深入看看转换结果
原始 JSX:
<div id="app">
<p>Hello</p>
<p>World</p>
</div>
转换后:
React.createElement(
"div",
{ id: "app" },
React.createElement("p", null, "Hello"),
React.createElement("p", null, "World")
);
看到没?JSX 的嵌套结构被转换成了嵌套的函数调用!🌳
🏗️ 虚拟 DOM:JSX 的“终极形态” 🧩
Babel 转换后的 React.createElement() 调用会生成一个 JS 对象,也就是所谓的 虚拟 DOM。
比如上面的例子会生成:
{
type: "h1",
props: {
className: "title",
children: ["Hello, JSX! 🎉"]
}
}
这个对象轻量、可操作,React 用它来高效地更新真实 DOM,避免频繁操作带来的性能损耗。⚡️
🧰 自定义组件是如何处理的?
JSX 不仅能写原生标签,还能写自定义组件:
function Welcome(props) {
return <h2>Welcome, {props.name}! 👋</h2>;
}
// 使用组件
const app = <Welcome name="Alice" />;
Babel 会这样转换:
const app = React.createElement(Welcome, { name: "Alice" });
注意:组件名必须大写,否则 Babel 会认为它是 HTML 标签(如 welcome 会被当作 <welcome> 处理)⚠️
🔌 Babel 是如何工作的?插件机制揭秘 🎛️
Babel 的核心工作流程是:
- 解析(Parse):把 JSX 源码转成抽象语法树(AST)🧩
- 转换(Transform):通过插件(如
@babel/plugin-transform-react-jsx)修改 AST - 生成(Generate):把 AST 重新生成标准 JavaScript 代码 📜
你可以把它想象成一个“翻译机器人”🤖:
你输入“你好”,它输出“Hello”——只不过这里的“语言”是编程语言。
🛠️ 动手实验:自己看看 Babel 的转换结果!
你可以访问 Babel 官方在线 REPL 来实时查看 JSX 的转换过程:
- 左边输入 JSX 代码
- 右边立刻看到转换后的 JavaScript
- 看看
React.createElement是如何被插入的!
👉 小实验:
<MyButton color="blue" onClick={handleClick}>
Click me!
</MyButton>
转换后:
React.createElement(MyButton, {
color: "blue",
onClick: handleClick
}, "Click me!");
是不是一目了然?🎯
💡 总结:JSX 的底层实现流程 🔄
| 步骤 | 说明 |
|---|---|
| 1️⃣ 写 JSX | 你用类 HTML 语法写组件 📝 |
| 2️⃣ Babel 解析 | Babel 将 JSX 转为 AST 🧩 |
| 3️⃣ 插件转换 | @babel/plugin-transform-react-jsx 将 JSX 节点转为 React.createElement() 调用 🔁 |
| 4️⃣ 生成 JS | 输出浏览器可执行的 JavaScript 📤 |
| 5️⃣ React 渲染 | createElement 返回虚拟 DOM,React 负责渲染到页面 🖼️ |
🚀 小贴士:JSX 的一些“潜规则” ⚠️
- ✅ 必须引入 React:即使你没直接用到
React,也要import React from 'react'(在 React 17+ 之后,新 JSX 转换已不需要) - ✅ 只能返回单个根元素:JSX 片段必须有一个父级(可以用
<>...</>Fragment 包裹) - ✅ JS 表达式用
{}:比如{name}、{2 + 2}、{isLogin ? 'Yes' : 'No'} - ❌ 不能使用
if语句:但可以用三元运算符或逻辑与&&
🌈 结语
JSX 让我们用更直观的方式构建 UI,而 Babel 在背后默默完成了“语言翻译”的工作。📚
理解 JSX 的底层实现,不仅能帮助你写出更规范的代码,还能在调试时更快定位问题。🐞
下次当你写下
<div>Hello</div>的时候,记得:
👉 它背后其实是React.createElement("div", null, "Hello")哦!😉
📌 延伸阅读: