0%

已完成 0/29 关

教学模式

你可以随时调整。

此设置调整教师的教学方式,无需重新生成课程。

第 6 关 含代码 约 10 分钟

使用 JSX 书写标签:闭合、单根节点与规则

使用 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 中:

  1. class 属性必须写成 className(因为 class 是 JavaScript 的保留关键字):
    <div className="main-box">内容</div>
  2. 带连字符的 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)。
下一站:JavaScript 与花括号:在 JSX 中动态显示数据与属性 →
© 2026 React 官方教程