第 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 对象 |
动手练习
- 渲染一个商品列表(数组),用 map
- 根据
isOnline状态显示在线/离线标签 - 用 Fragment 包裹两个兄弟元素
- 故意用
0 && <X />,观察渲染结果,然后修复
推荐阅读
下一章:第 145 章:组件与 Props →