第 2 章:JSX 语法
学习目标
- 理解 JSX 是 JavaScript 的语法扩展
- 掌握 JSX 表达式、属性、子节点写法
- 区分 JSX 与 HTML 的 4 个关键差异
- 避开 JSX 的 5 个常见坑
一、JSX 是什么?
JSX = JavaScript + XML 语法。它让你在 JS 里直接写类似 HTML 的代码,最终被 Babel 编译成 React.createElement() 调用。
tsx
const element = <h1 className="title">Hello</h1>;
// 编译后:
// const element = React.createElement('h1', { className: 'title' }, 'Hello');JSX 不是字符串、不是 HTML,是 JS 的语法糖。
二、JSX 表达式(插值)
用 {} 嵌入任意 JS 表达式:
tsx
function Greeting() {
const name = 'React';
const score = 95;
return (
<div>
<p>你好,{name}</p>
<p>分数:{score + 5}</p>
<p>当前时间:{new Date().toLocaleString()}</p>
<p>结果:{score >= 60 ? '及格' : '不及格'}</p>
</div>
);
}可以放:
- 变量、字面量
- 三元、函数调用、对象属性
- JSX 本身
不能放:
if/else语句块for循环- 普通对象(除非是 React 元素)
三、属性写法
tsx
function Avatar() {
return (
<img
src="https://example.com/a.png"
alt="头像"
className="avatar" // ⚠️ 不是 class,是 className
tabIndex={0} // ⚠️ 不是 tabindex,是 tabIndex
style={{ width: 100, height: 100 }} // style 是对象
onClick={() => alert('hi')} // 事件 onClick(驼峰)
/>
);
}⚠️ 坑 1:JSX 更接近 HTML 属性 →
class改成className,for改成htmlFor,tabindex改成tabIndex。 ⚠️ 坑 2:style必须是对象。
四、条件渲染
写法 1:三元运算符(最常用)
tsx
function Status({ isLogin }: { isLogin: boolean }) {
return <div>{isLogin ? <p>已登录</p> : <p>请登录</p>}</div>;
}写法 2:与运算符 &&(只关心 true 情况)
tsx
function Notice({ count }: { count: number }) {
return <div>{count > 0 && <p>你有 {count} 条未读消息</p>}</div>;
}写法 3:立即执行函数
tsx
function Panel({ mode }: { mode: 'a' | 'b' }) {
return (
<div>
{(() => {
if (mode === 'a') return <p>A 模式</p>;
return <p>B 模式</p>;
})()}
</div>
);
}⚠️ 坑 3:
count && <X />当count = 0时,会渲染数字 0 到页面上!必须写count > 0 && <X />。
五、列表渲染
用 map 把数组转成 JSX,必须设置 key:
tsx
function TodoList() {
const todos = [
{ id: 1, text: '学 React' },
{ id: 2, text: '写组件' },
{ id: 3, text: '部署上线' },
];
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.text}</li> // �️ key 必须唯一且稳定
))}
</ul>
);
}⚠️ 坑 4:key 不要用数组
index,列表增删改时 index 会变,导致性能问题和组件状态错乱。用业务 ID。 ⚠️ 坑 5:每个 map 返回的 JSX 列表必须有 key,控制台会爆红警告。
六、Fragment 片段
返回多个元素时,不想多包一层 div,用 <>...</> 或 <Fragment>:
tsx
import { Fragment } from 'react';
function TwoCols() {
return (
<>
<h1>左</h1>
<h1>右</h1>
</>
);
}带 key 时必须用完整写法:
tsx
function List() {
return (
<Fragment>
{items.map((item) => (
<Fragment key={item.id}>
<dt>{item.name}</dt>
<dd>{item.desc}</dd>
</Fragment>
))}
</Fragment>
);
}七、JSX 防注入
React 默认对所有字符串插值做转义,<script>alert(1)</script> 这种字符串渲染出来是文本,不会执行。
tsx
const userInput = '<script>alert(1)</script>';
return <div>{userInput}</div>; // 安全,只显示字符串但 dangerouslySetInnerHTML 会跳过转义,慎用:
tsx
<div dangerouslySetInnerHTML={{ __html: '<b>html</b>' }} />八、本章小结
| 要点 | 关键 |
|---|---|
| JSX 本质 | 编译为 React.createElement |
| 插值 | {} 内放 JS 表达式,不能放语句 |
| 属性 | className/htmlFor/驼峰事件 |
| 条件渲染 | 三元 / && / 立即执行函数 |
| 列表渲染 | map + 唯一 key,不用 index |
| Fragment | <> 避免多余 DOM 包裹 |
| 安全 | 默认转义,除非显式 dangerouslySetInnerHTML |
动手练习
- 用户卡片:渲染
[{name, age}]列表,大于 18 显示「成年」,否则「未成年」 - 状态徽章:写一个组件,根据
status: 'success'|'error'|'pending'显示不同颜色 div - 修复 bug:找出下面代码的 2 个坑并改对tsx
{ items.map((item, i) => <li key={i}>{item}</li>); } { count && <Badge count={count} />; }
下一章:第 3 章:组件与 Props →