人人都会AI编程

2.3 JSX 语法核心规则

更新时间:2026-07-10

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 赋值给变量、作为函数返回值、放在 iffor 循环中,因为它在编译后就是一个普通的 JavaScript 表达式。
  • 空标签 <></>Fragment 的语法糖,后面会细讲。

表达式嵌入:在 JSX 中使用 JavaScript

在 JSX 中,你可以用花括号 {} 包裹任意 JavaScript 表达式(注意,是表达式,不是语句)。表达式会产生一个值,因此你可以放入变量、函数调用、算术运算、三元运算符等,但不能放入 iffor 等语句。

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 用对象
  • 必须有根节点,没必要的用 <>...</>
  • 注释放在 {/ /}

这些规则看起来不复杂,却是避免大量低级错误的关键。