使用 JSX 书写标签:闭合、单根节点与规则
Web 最初是建立在 HTML、CSS 和 JavaScript 之上的。多年以来,Web 开发者把网页结构放在 HTML 中,样式放在 CSS 中,逻辑放在 JavaScript 中——这被称为“关注点分离”。但随着网页越来越具动态交互性,逻辑往往直接决定了标签的内容!
这就是为什么在 React 中,渲染逻辑与标签协同生活在同一个地方——组件内部。
JSX(JavaScript XML)就是为了实现这一结合而诞生的语法扩展。它长得很像 HTML,但比 HTML 更加严格,并且拥有 JavaScript 的完整计算能力。
1. JSX 的三大核心规则
如果你直接把一段旧网页的 HTML 代码复制粘贴到 React 组件中,通常会报错。要让它成为合法的 JSX,必须遵守以下三大规则:
规则一:只能返回一个根元素(Return a single root element)
如果你想从一个组件返回多个标签,必须用一个单一的父标签把它们包裹起来:
// ❌ 错误:不能同时返回并列的两个顶级元素
export default function TodoList() {
return (
<h1>待办事项</h1>
<p>今天需要完成的任务</p>
);
}
// ✅ 正确:使用 <div> 包裹
export default function TodoList() {
return (
<div>
<h1>待办事项</h1>
<p>今天需要完成的任务</p>
</div>
);
}
如果不想在 HTML 里增加多余的 <div> 怎么办?
如果你使用 <div> 包裹,最终渲染出来的浏览器 DOM 就会多出一层 <div>,有时这会破坏 CSS Flexbox 或 Grid 的排版。
此时,请使用 Fragment(空标签,写作 <>...</>):
// ✅ 正确:使用 Fragment 包裹,不会向 DOM 添加任何多余元素
export default function TodoList() {
return (
<>
<h1>待办事项</h1>
<p>今天需要完成的任务</p>
</>
);
}
为什么 JSX 必须有一个根节点? JSX 看起来像 HTML,但它在编译后会被转化为普通的 JavaScript 函数调用(例如
React.createElement)。 在 JavaScript 中,一个函数不可能同时返回两个值(你不能写return a, b并指望得到两个独立的返回值)。因此,必须返回一个包含所有子项的单一对象或数组。
规则二:对所有标签进行闭合(Close all the tags)
在传统的 HTML 中,写 <img src="...">、<input> 或 <br> 时经常不闭合标签。
但在 JSX 中,所有标签必须显式闭合:
- 自闭合标签:必须以
/>结尾,例如<img />、<input />、<br />; - 成对标签:必须有配对的闭合标签,例如
<li>任务内容</li>。
// ❌ 错误
<img src="avatar.jpg" alt="头像">
<li>吃饭
<li>睡觉
// ✅ 正确
<img src="avatar.jpg" alt="头像" />
<li>吃饭</li>
<li>睡觉</li>
规则三:绝大部分属性采用驼峰式命名法(camelCase most of the things!)
JSX 最终会转换为 JavaScript 代码,而 JSX 中写的属性最终会变成 JavaScript 对象的键名。
但在 JavaScript 中,变量名或对象属性名有一些特殊限制(例如不能包含连字符 -,不能是保留关键字)。
因此,在 JSX 中:
class属性必须写成className(因为class是 JavaScript 的保留关键字):<div className="main-box">内容</div>- 带连字符的 HTML 属性转为小驼峰命名(camelCase):
stroke-width变成strokeWidth;tabindex变成tabIndex;onclick变成onClick。
例外情况:
aria-*(无障碍访问属性)和data-*(自定义数据属性)依然保留带连字符的原生 HTML 拼写,例如aria-label="关闭"。
实战演练:修复一段旧 HTML
看看下面这段有问题的代码:
<h1>Hedy Lamarr 的待办事项</h1>
<img src="https://i.imgur.com/yXOvdOSs.jpg" alt="Hedy Lamarr" class="photo">
<ul>
<li>发明新式红绿灯
<li>排练电影场景
<li>改进频谱通信技术
</ul>
将其转化为规范纯净的 React JSX:
export default function TodoList() {
return (
<>
<h1>Hedy Lamarr 的待办事项</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>
<ul>
<li>发明新式红绿灯</li>
<li>排练电影场景</li>
<li>改进频谱通信技术</li>
</ul>
</>
);
}
总结
- JSX 让标签和逻辑有机结合在组件中;
- 必须用一个父元素或 Fragment(
<>...</>)包裹单根节点; - 所有单标签必须显式自闭合(
<img />); - HTML 属性在 JSX 中绝大部分转换为驼峰命名(如
className)。