JSX 语法

JSX 是 React 的招牌特性——一种"语法扩展",让你在 JavaScript 里写 HTML 风格的标签。乍看不习惯,但它把"组件长什么样"和"组件有什么逻辑"放在同一个文件里,实际开发效率极高。这一章我们彻底搞懂 JSX 的所有规则。

1. JSX 长什么样

JSX 看起来像 HTML,但它是写在 JavaScript 文件里的:

// JSX:在 JS 里写类似 HTML 的语法
const element = <h1>你好,React!</h1>;

// 这不是字符串,也不是 HTML,是一种"语法扩展"
// 最终会被构建工具编译成下面这种 JS 调用:
// React.createElement("h1", null, "你好,React!")

你可能会问:为什么不直接用字符串拼接生成 HTML?因为字符串拼接又慢又难维护(转义、嵌套、XSS 全是坑)。JSX 在构建时会被编译成 React.createElement 调用,最终生成一个普通 JS 对象(虚拟 DOM),React 拿它去高效更新真实 DOM。你写 JSX,React 帮你管 DOM——这就是声明式渲染的本质。

2. 花括号嵌入表达式

JSX 最强大的特性:用花括号把任何 JavaScript 表达式嵌进标签里。这是它和 HTML 模板(如 Vue 的 mustache)最大的不同——花括号里是真正的 JS,不是字符串。

// 花括号里可以嵌入任何 JS 表达式
const name = "小明";
const age = 20;
const greeting = <p>你好,{name},今年 {age} 岁。</p>;

// 表达式可以是任意运算
const total = <p>总价:{10 + 5 * 2} 元</p>;

// 也可以是三元、函数调用
const isAdmin = true;
const tag = <span>{isAdmin ? "管理员" : "访客"}</span>;

// 注意:语句(if、for)不能放在花括号里,只能放表达式

关键区别:表达式(有返回值的,如变量、运算、三元、函数调用)能放;语句(if、for、switch)不能放。需要条件判断就用三元,需要循环就用后面讲的 map

3. 属性写法的特殊之处

JSX 属性和 HTML 类似,但有几个地方必须改:

// 1. class 是 JS 关键字 → 用 className
<div className="card highlight">内容</div>

// 2. style 要写成对象(双花括号:外层 JSX 表达式,内层样式对象)
<div style={{ color: "red", fontSize: "14px" }}>
  红色文字
</div>

// 3. 属性名用驼峰:onclick → onClick,maxlength → maxLength
<button onClick={handleClick}>点我</button>
<input maxLength={20} />

// 4. 布尔属性:true 写属性名,false 省略
<input disabled={true} />
<input disabled />            {/* 等价 */}
<input disabled={false} />   {/* 等于不写 */}

这些"奇葩"写法的根本原因是:JSX 是 JS 不是 HTML。JS 里 class 是保留字(虽然现在没用了),所以改叫 className;HTML 属性 onclick 全小写,JS 里约定驼峰,所以变成 onClick。记住这两条,后面就不会踩坑。

4. 条件渲染的三种写法

React 里没有 v-if 这种专用指令,条件渲染全部用 JS 本身的语法解决。常用三种:

// 方式 1:三元表达式(二选一)
function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? <h1>欢迎回来!</h1> : <h1>请登录</h1>}
    </div>
  );
}

// 方式 2:逻辑与 && (条件为真才渲染)
function Mailbox({ unreadMessages }) {
  return (
    <div>
      <h1>邮箱</h1>
      {unreadMessages.length > 0 && (
        <p>你有 {unreadMessages.length} 条未读消息</p>
      )}
    </div>
  );
}

// 方式 3:提前 return(整个组件提前结束)
function Warning({ isDanger }) {
  if (!isDanger) return null;   // 不渲染任何东西
  return <div className="warning">危险!</div>;
}

新手最容易写错的,是在花括号里放 if:

5. 一个组件只能返回一个根元素

JSX 有个硬规则:一个组件的 return 语句只能有一个最外层标签。下面这样会报错:

但有时候我们不想多加一个 div(会破坏布局或语义),这时就用 Fragment——它是一个"透明容器",渲染时不产生真实 DOM 节点:

import { Fragment } from "react";

// 方式 1:用 <Fragment> 包裹(可以传 key)
function ItemList({ items }) {
  return items.map(item => (
    <Fragment key={item.id}>
      <dt>{item.term}</dt>
      <dd>{item.desc}</dd>
    </Fragment>
  ));
}

// 方式 2:用空标签 <></>(语法糖,但不能传 key)
function TwoLines() {
  return (
    <>
      <h2>标题</h2>
      <p>正文段落</p>
    </>
  );
}

日常用空标签 <></> 居多,简短又清晰。需要传 key(比如在 map 里)时才用完整写法 <Fragment>

6. JSX 也是表达式

这是理解 React 的关键:JSX 不是模板字符串,而是一个真正的 JS 值。它可以赋值、传参、返回:

// JSX 本身也是一个 JS 表达式
const elem = <h1>标题</h1>;

// 可以赋值给变量
const header = <header>{elem}</header>;

// 可以做函数参数
function render(component) {
  return <main>{component}</main>;
}
render(<h1>传入的标题</h1>);

// 可以做返回值(这就是组件)
function Title() {
  return <h1>函数返回的 JSX</h1>;
}

"组件就是返回 JSX 的函数"——这句话的根基就在这里。当你写 function App() { return <h1>...</h1> },本质就是定义了一个返回特定对象的函数。

7. 注释写法

JSX 里的注释有点特殊——既不是 HTML 注释也不是 JS 单行注释,而是花括号包裹的多行注释:

小结

JSX 是 React 的"门面":看起来像 HTML,本质是 JS。记住四条铁律——花括号放表达式不放语句、class 改 className、属性名驼峰、单根返回用 Fragment——你就能看懂和写出任何 JSX。下一篇我们看 JSX 的"宿主":函数组件

← 上一篇 React 环境搭建

下一篇 React 函数组件

✈️💬