Skip to content
第 144 / 250 章前端⏱ 10 分钟阅读

第 144 章:JSX 语法详解

学习目标

  • 掌握 JSX 表达式嵌入规则
  • 理解条件渲染与列表渲染
  • 学会使用 Fragment
  • 避免 JSX 常见陷阱

一、JSX 是什么

JSX 是 JavaScript 的语法扩展,看起来像 HTML,但其实是 React.createElement 的语法糖。

tsx
// JSX
const element = <h1 className="title">Hello, React!</h1>;

// 编译后
const element = React.createElement(
  'h1',
  { className: 'title' },
  'Hello, React!'
);

二、表达式嵌入

2.1 用 {} 嵌入 JS 表达式

tsx
const name = 'Tom';
const age = 18;

return (
  <div>
    <p>姓名:{name}</p>
    <p>年龄:{age + 1}</p>
    <p>明年:{age >= 18 ? '成年' : '未成年'}</p>
    <p>{`我叫${name},今年${age}岁`}</p>
  </div>
);

2.2 不能放语句

tsx
// ❌ 错误:JSX 中不能放 if/for 等语句
return (
  <div>
    {if (age > 18) { return '成年'; }}    // 错
    {for (let i = 0; i < 3; i++) {}}       // 错
  </div>
);

// ✅ 正确:用表达式或 IIFE
return (
  <div>
    {age > 18 ? '成年' : '未成年'}
    {(() => { /* IIFE */ })()}
  </div>
);

三、条件渲染

3.1 三元

tsx
{isLoggedIn ? <UserPanel /> : <LoginButton />}

3.2 与运算符 &&

tsx
{unreadCount > 0 && <Badge count={unreadCount} />}
{error && <p className="text-red-500">{error}</p>}

注意:0 && x 结果是 0,会直接渲染数字 0。要么显式布尔转换 {count > 0 && ...},要么用 !!

3.3 立即函数

tsx
{(() => {
  if (status === 'loading') return <Spinner />;
  if (status === 'error') return <Error />;
  return <Content />;
})()}

四、列表渲染

4.1 map 渲染

tsx
const items = ['苹果', '香蕉', '橙子'];

return (
  <ul>
    {items.map((item, index) => (
      <li key={index}>{item}</li>
    ))}
  </ul>
);

4.2 key 必填

tsx
// ❌ 警告
{items.map(item => <li>{item}</li>)}

// ✅ 正确
{items.map(item => <li key={item}>{item}</li>)}
{items.map((item, i) => <li key={i}>{item}</li>)}

key 应该是稳定、唯一的 ID,不要用 index(列表会重排时会出问题)。

4.3 对象数组

tsx
interface User { id: number; name: string; }
const users: User[] = [
  { id: 1, name: 'Tom' },
  { id: 2, name: 'Jerry' },
];

return (
  <ul>
    {users.map(user => (
      <li key={user.id}>
        {user.id} - {user.name}
      </li>
    ))}
  </ul>
);

五、Fragment 片段

5.1 不用 Fragment 会报多根错误

tsx
// ❌ 多根节点
return (
  <h1>标题</h1>
  <p>内容</p>
);

// ✅ 用 Fragment
return (
  <>
    <h1>标题</h1>
    <p>内容</p>
  </>
);

// ✅ 短语法糖
return (
  <Fragment>
    <h1>标题</h1>
    <p>内容</p>
  </Fragment>
);

5.2 带 key 的 Fragment

tsx
import { Fragment } from 'react';

{items.map(item => (
  <Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.description}</dd>
  </Fragment>
))}

六、属性规则

6.1 className vs class

tsx
// React 用 className(JS 关键字 class 冲突)
<div className="container">...</div>

6.2 htmlFor vs for

tsx
<label htmlFor="email">邮箱</label>
<input id="email" type="email" />

6.3 布尔属性

tsx
<input disabled />           // 等价 disabled={true}
<input />                    // 等价 disabled={false}
<button aria-hidden="true" />

6.4 style 对象

tsx
<div style={{ color: 'red', fontSize: 16, marginTop: '10px' }}>
  内容
</div>

七、子元素

tsx
// 字符串
<MyComponent>hello</MyComponent>

// JSX
<MyComponent>
  <h1>标题</h1>
  <p>段落</p>
</MyComponent>

// 表达式
<MyComponent>{`hello ${name}`}</MyComponent>

// 数组(自动展开)
<MyComponent>{['a', 'b', 'c']}</MyComponent>

八、常见陷阱

8.1 注释写法

tsx
return (
  <div>
    {/* JSX 中注释这样写 */}
    <p>内容</p>
  </div>
);

8.2 标签必须闭合

tsx
// ❌
<img src="x.png">
<br>

// ✅
<img src="x.png" />
<br />

8.3 不要在 JSX 中调用 Hook

tsx
// ❌
return (
  <div>{useState(0)}</div>
);

// ✅
const [count] = useState(0);
return <div>{count}</div>;

九、本章小结

概念关键
表达式{} 嵌入 JS 表达式,不能放语句
条件三元 / && / IIFE
列表map + 稳定 key
片段<>...</>
属性className / htmlFor / style 对象

动手练习

  1. 渲染一个商品列表(数组),用 map
  2. 根据 isOnline 状态显示在线/离线标签
  3. 用 Fragment 包裹两个兄弟元素
  4. 故意用 0 && <X />,观察渲染结果,然后修复

推荐阅读


下一章:第 145 章:组件与 Props

本站基于 VitePress 构建 · 由 Codebook 团队维护