大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
JSX 与渲染
这一课你将能做到
- 认出 JSX,并知道它最终还是 JavaScript
- 正确使用
className、{}插入表达式 - 给列表每一项写上稳定的
key(并知道为什么)
为什么要学
React 组件要返回「界面长什么样」。
用 JSX 写起来像 HTML,但其实是 JS 语法扩展,构建工具会把它编译成函数调用。
人话:你在 JS 文件里写长得像标签的东西,方便描述界面;浏览器真正执行的仍是 JS。
常见卡点:把 JSX 当成「另一个 HTML 文件」,结果在里面写 class、忘记最外层只能有一个根、把整个对象丢进 {user}。本站卡片标题、进度数字,全是 JSX 里用 {} 插进去的。
先建立图像
const title = "题库";
const el = <h1 className="title">{title}</h1>;
人话拆开看:
- 外表像标签
<h1>...</h1> {title}是插入 JS 表达式(这里是变量)className对应 HTML 的class(因为class在 JS 里是关键字,不能当属性名随便用)
编译后的直觉(不必手写):大约变成 createElement("h1", { className: "title" }, title)。所以 JSX 不是魔法 HTML,是糖衣。
跟着做
步骤 1:根节点(最常见语法报错)
JSX return 时,最外层需要一个根,或用空标签 Fragment:
// 可以:隐形包裹,不增加多余 DOM
return (
<>
<h1>本站路径</h1>
<p>前端主线</p>
</>
);
// 也可以包一层真实 div
return (
<div>
<h1>本站路径</h1>
<p>前端主线</p>
</div>
);
相邻两个标签并排 return、外面不包,会语法报错。人话:函数一次只能「交回一件东西」;Fragment 就是那件隐形袋子。
步骤 2:表达式与「不能直接渲染对象」
const user = { name: "ada" };
export function Who() {
// return <p>{user}</p>; // 报错:对象不能当 React 孩子
return <p>{user.name}</p>; // 正确:渲染字符串
}
规则记三条:
{}里放表达式(变量、三元、函数调用),不能直接放if语句- 布尔值
true/false、null、undefined通常什么都不显示 - 数字
0要小心:条件渲染课会讲{0 && <X />}会把 0 画出来
步骤 3:列表与 key(本站课时列表就是这个)
const lessons = [
{ id: 1, title: "HTML" },
{ id: 2, title: "CSS" },
];
export function LessonTitles() {
return (
<ul>
{lessons.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
);
}
key 人话:帮 React 认出「哪一项是哪一项」,好做增删、重排时的更新。
用业务里稳定的 id。列表会插入/删除/排序时:
- 不要用
Math.random()(每次渲染都变,React 以为全是新项) - 慎用纯下标
index(中间插一项,后面的下标全变,容易绑错状态)
步骤 4:属性和样式最小集
function Chip({ active, label }) {
return (
<span
className={active ? "chip on" : "chip"}
style={{ marginRight: 8 }}
tabIndex={0}
>
{label}
</span>
);
}
- 属性名驼峰:
onClick、tabIndex、htmlFor(对应for) style是对象,CSS 属性也驼峰:marginRight不是margin-right- 日常更常用
className写样式,内联 style 留给动态一两个值
步骤 5:表达式与「不能放对象」
const done = true;
return <p>{done ? "完成" : "未完成"}</p>;
// return <p>{{ a: 1 }}</p>; // 对象不是合法 React 子节点
应看到:{} 里放表达式;原始类型/元素/数组 OK,随意对象会报错。
讲清楚要点
- JSX 仍是 JS:文件是
.jsx/.tsx,逻辑用 JS,不要幻想「另一个模板语言无限自由」。 - 自定义组件名必须大写开头:
<StationCard />;小写<div>、<span>当原生标签。 - 注释只能写成
{/* 这样 */},不要写 HTML 的<!-- -->。 - 一个表达式一个
{}:{a}{b}可以;{a + b}也行。不要写成{{ a }}除非你真的要对象(通常不要当孩子)。 - 布尔属性:
<input disabled={saving} />,不要学 HTML 只写disabled却搞不清何时关闭。
你可能会懵的地方
-
为何不是
class?
JS 保留字冲突,React 约定className。写错class时有的工具会警告。 -
{}里能写 if 吗?
不能直接写语句。先在函数体里用 if 算出let node = ...,再{node};或用三元。 -
Fragment
<>是什么?
不额外生成 DOM 节点的隐形包裹。需要 key 时用<Fragment key={id}>(从 react 导入)。 -
为何有时必须
{" "}?
JSX 里换行空格容易被吃掉;两个元素之间要空格可显式插入。 -
class为什么写成className?
class是 JS 保留字;SVG 里strokeWidth等亦驼峰。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 列表不写 key | 控制台警告、更新错乱 | 稳定 id 作 key |
渲染整个对象 {user} | 运行报错 | 渲染 user.name 等字段 |
组件名小写 <card /> | 被当成未知 HTML 标签 | Card 大写 |
return <h1/><p/> 双根 | 语法错误 | Fragment 或一个父节点 |
style="color:red" 字符串 | 不是 HTML 文件 | style={{ color: "red" }} |
| 多个根又不包 Fragment | 编译/语法错 | <>...</> 或单父节点 |
自测三问
className和class为什么不同?{title}里的大括号表示什么?能不能写if?- key 主要帮 React 解决什么问题?为什么随机数不行?
小结一句话
JSX 用类 HTML 的写法描述 UI,插入数据用 {},列表务必给稳定 key。
动手加深
把一段 createElement 手写与等价 JSX 对照抄一遍,确认 JSX 不是字符串 HTML。
延伸
- react.dev Describing the UI
- 下一课:函数组件——把 JSX 装进可复用函数。