JSX(JavaScript XML)是 React 中描述 UI 结构的语法扩展。它看起来像 HTML,但本质上是 JavaScript。理解 JSX 的几个核心规则,是流畅编写 React 组件的基础。
JSX 的本质与编译原理
浏览器无法直接执行 JSX,它必须经过编译工具(如 Babel 或 Vite 内置的 esbuild)转换成标准的 JavaScript 函数调用。每一段 JSX 最终都会被翻译成 React.createElement(type, props, ...children) 或等效的 jsx() 调用(React 17+ 新 JSX 转换)。
// 你写的 JSX
<h1 className="title">Hello {name}</h1>
// 编译后的结果(简化版,React 17 之前)
React.createElement('h1', { className: 'title' }, 'Hello ', name);
// React 17+ 新 JSX 转换(无需显式引入 React)
import { jsx } from 'react/jsx-runtime';
jsx('h1', { className: 'title', children: ['Hello ', name] });
理解这个编译原理有两个实际好处:
- JSX 是表达式:你可以把 JSX 赋值给变量、作为函数返回值、放在
if或for循环中,因为它在编译后就是一个普通的 JavaScript 表达式。 - 空标签
<></>是Fragment的语法糖,后面会细讲。
表达式嵌入:在 JSX 中使用 JavaScript
在 JSX 中,你可以用花括号 {} 包裹任意 JavaScript 表达式(注意,是表达式,不是语句)。表达式会产生一个值,因此你可以放入变量、函数调用、算术运算、三元运算符等,但不能放入 if、for 等语句。
function Greeting({ user }) {
// ✅ 表达式
const formattedName = user.firstName + ' ' + user.lastName;
return (
<div>
<h1>Hello, {formattedName}!</h1>
<p>年龄:{user.age >= 18 ? '成年' : '未成年'}</p>
<p>今日幸运数:{Math.floor(Math.random() * 100)}</p>
</div>
);
}
常见误用:
// ❌ 不能放语句
<p>{if (user.vip) { return 'VIP用户' }}</p>
// ✅ 改用三元运算符
<p>{user.vip ? 'VIP用户' : '普通用户'}</p>
属性绑定
JSX 的属性同样使用花括号动态绑定 JavaScript 值,也可以用引号直接写字符串字面量。
const imageUrl = 'https://example.com/avatar.png';
const altText = '用户头像';
// 字符串字面量(引号)
<img src="https://example.com/logo.png" />
// 动态绑定(花括号)
<img src={imageUrl} alt={altText} />
// 数字、布尔值也需要用花括号
<input type="number" min={0} max={100} disabled={isDisabled} />
几个属性命名的特殊规则:
- HTML 的
class属性在 JSX 中写作className,因为class是 JavaScript 的保留字。 for属性写作htmlFor(用于 label 元素)。- 内联样式使用驼峰命名的对象,而非 CSS 字符串。
class 与 style 写法
class 写法:
// 静态类名
<div className="container box">
// 动态类名拼接(常见做法)
<div className={`item ${isActive ? 'active' : ''}`}>
// 使用第三方工具库 classnames 更优雅
import cn from 'classnames';
<div className={cn('item', { active: isActive, disabled: !isEnabled })}>
style 写法:
内联样式接收一个 JavaScript 对象,属性名用驼峰命名,值可以是数字(自动加 px)或字符串。
<div style={{
backgroundColor: 'lightblue',
fontSize: 16, // 相当于 '16px'
marginTop: 10,
display: user ? 'block' : 'none'
}}>
内容
</div>
注意,内联样式不会自动添加厂商前缀,对于需要前缀的属性建议使用 CSS 类或工具库(如 Radium)。
根元素与碎片(Fragment)
JSX 表达式必须有单一的根节点,不能返回多个平级的顶层元素。
// ❌ 错误,两个顶层元素
function Component() {
return (
<h1>标题</h1>
<p>内容</p>
);
}
// ✅ 方案1:包裹在一个父元素中
function Component() {
return (
<div>
<h1>标题</h1>
<p>内容</p>
</div>
);
}
但有时我们不希望额外生成多余的 DOM 节点(比如在表格中插入 <tr>,或避免破坏 Flex/Grid 布局)。React 提供了 Fragment(碎片)来充当不可见的包裹元素,它不会渲染任何真实 DOM。
// 显式写法
import { Fragment } from 'react';
function Component() {
return (
<Fragment>
<h1>标题</h1>
<p>内容</p>
</Fragment>
);
}
// 语法糖:空标签对(推荐)
function Component() {
return (
<>
<h1>标题</h1>
<p>内容</p>
</>
);
}
<></> 和 <Fragment> 的区别在于,如果需要给 Fragment 加 key 属性(比如在列表渲染中),必须使用 <Fragment key={...}>。
JSX 中的注释
直接在 JSX 中写 // 或 / / 会被当作普通文本渲染。正确的注释写法是放在花括号内,使用 JavaScript 的多行注释语法 / /。
<div>
{/* 这是一个注释,不会渲染 */}
<h1>Hello</h1>
{/*
多行注释
也是可以的
*/}
</div>
注意:单行注释 // 在 jsx 的花括号里不太方便,因为 // 会注释掉后面的 },通常使用 {/ /}。
小结
JSX 将标记与逻辑放在一起,让组件的结构更加内聚。掌握这些核心规则,你就具备了熟练编写 JSX 的能力:
- 把 JSX 看作普通的 JavaScript 表达式
- 用
{}嵌入表达式,属性名使用驼峰 - class 用
className,style 用对象 - 必须有根节点,没必要的用
<>...</> - 注释放在
{/ /}中
这些规则看起来不复杂,却是避免大量低级错误的关键。