首页 / 文章 / React JSX 看起来像 HTML——它会被编译成 JavaScript

React JSX 看起来像 HTML——它会被编译成 JavaScript

JSX是JavaScript标记语言:具有单一根节点、封闭标签、驼峰式属性名,以及用于表达式的花括号——这些都是初学者最先接触到的基本规则。

1208 词

在组件文件中,JSX 看起来像 HTML,但它会被编译成 JavaScript——这一事实几乎可以解释所有的语法规则。

前面的章节通常将组件返回的标记——例如 <h1>Hello</h1>——视为非正式的“标记”。它的真正名称是 JSX。这里的目标很简单:把 JSX 当作看起来像 HTML 的 JavaScript 来看待。一旦明确了这一点,大多数表面上的语法规则就不再显得随意了。

为何选择 JSX——将标记与逻辑结合

传统的前端开发方式是将结构放在 HTML 文件中,将行为放在 JavaScript 文件中。实际上,按钮的外观与点击处理函数很难分开处理。React 将两者都放在同一个单元——即组件——中,因此需要一种在 JavaScript 中编写标记的方式,而这种语法就是 JSX。

从外观上看,它与HTML很相似。由于编译器会将JSX转换为JavaScript,因此其规则比HTML更为严格。首先来了解初学者需要掌握的三条规则。

JSX的三条规则

规则1 —— 一个根节点

一个组件只能返回一个JSX树。多个同级元素必须位于同一个父节点之下。

// 🔴 two siblings side by side — error
return (
  <h1>Title</h1>
  <p>Content</p>
);

// ✅ wrapped in one
return (
  <div>
    <h1>Title</h1>
    <p>Content</p>
  </div>
);

如果额外的<div>会导致DOM结构混乱,可将这些同级元素包裹在Fragment中(<>...</>)。

return (
  <>
    <h1>Title</h1>
    <p>Content</p>
  </>
);

为什么要求只有一个根节点?因为JSX最终会变成JavaScript值。函数无法直接返回两个值,必须将它们封装起来;JSX也是如此。“这是JavaScript,不是HTML”这一概念使得这条规则合理存在。

规则2 —— 每个标签都必须闭合

HTML允许某些标签如<img>或<br>不使用闭合标签。而在JSX中每个标签都必须闭合。没有子内容的标签则使用自闭合形式/>。

// 🔴 an HTML habit — error
<img src="logo.png">
<input type="text">

// ✅ self-closing
<img src="logo.png" />
<input type="text" />

规则3 — camelCase命名法

JSX中的属性会成为JavaScript对象的关键字,因此其命名需遵循JavaScript的规范(camelCase),而非原始HTML的命名方式。常见的保留字冲突包括:

  • class → className(class在JavaScript中是保留字)
  • for → htmlFor(for也是保留字)
// 🔴 straight from HTML
<div class="card">
  <label for="email">Email</label>
</div>

// ✅ the JSX way
<div className="card">
  <label htmlFor="email">Email</label>
</div>

同样的映射规则会将onclick转换为onClick,将tabindex转换为tabIndex。无需记住完整列表:编辑器自动补全功能以及TypeScript能快速识别出错误的名称。

花括号{}——JSX中的JavaScript

正是在这里,JSX才不再仅仅是静态模板。由于JSX本身就是JavaScript,花括号{}可用于直接嵌入JavaScript值。HTML只能存储固定文本,而JSX则可以计算表达式。

function Greeting() {
  const name = 'Jane';
  return <h1>Hello, {name}!</h1>; // inside the braces is JavaScript
}

{name}会被替换为name的值(即'Jane')。花括号可以接受任何表达式——也就是能够计算出数值的代码,而不仅仅是单纯的变量。

function Bill() {
  const price = 12000;
  const count = 3;
  return (
    <div>
      <p>Unit price: {price} won</p>
      <p>Quantity: {count}</p>
      <p>Total: {price * count} won</p>          {/* arithmetic works too */}
      <p>{new Date().getFullYear()} receipt</p>  {/* function calls work too */}
    </div>
  );
}

花括号也可用于属性值中。在这种用法下,无需在花括号周围加引号。

function Avatar() {
  const user = { name: 'Jane Kim', imageUrl: '/me.png' };
  return <img src={user.imageUrl} alt={user.name} />;
}
  • src="..." — 引号表示字面字符串。
  • src={...} — 大括号表示JavaScript值。

仅允许使用表达式

在大括号内可以放置能生成值的表达式,但不能放置if、for或const之类的语句。

// 🔴 an if statement isn't an expression, so this fails
<p>{if (count > 0) 'in stock'}</p>

// ✅ the ternary operator is an expression, so it works
<p>{count > 0 ? 'in stock' : 'out of stock'}</p>

使用三元运算符、&&及相关模式实现的条件UI应放在专门的条件渲染章节中。目前的原则是:只有能生成值的代码才能放入大括号内。

双大括号{{ }} — 大括号内的对象

在 JSX 中经常出现的 {{ }} 模式并非独立的语言特性,而是嵌在外层括号内的JavaScript 对象 { },内联样式就是典型的例子。

// outer { } = "I'm putting JavaScript here"
// inner { } = the object itself
<div style={{ color: 'tomato', fontSize: 20 }}>styled</div>

HTML 会使用类似 style="color: tomato" 的字符串形式,而 JSX 则传递的是对象,因此属性名采用驼峰式命名(如 fontSize 而非 font-size),其值通常为字符串或数字。如果双大括号看起来难以理解,可先将其拆分为带名称的变量。

const cardStyle = { color: 'tomato', fontSize: 20 };
<div style={cardStyle}>styled</div>   // exactly the same as above

外层括号表示“在此处插入 JavaScript 值”,而内部对象即为该值。

如何编写注释

JSX 中的注释也使用括号:即用 {} 包裹的 JavaScript 块级注释。

return (
  <div>
    {/* this is a comment inside JSX */}
    <h1>Title</h1>
  </div>
);

综合应用

上述规则都可以放在一个产品卡片组件中。

// src/ProductCard.tsx
function ProductCard() {
  const product = {
    name: 'Mechanical Keyboard',
    price: 89000,
    inStock: true,
    imageUrl: "https://picsum.photos/200/300",
  };

  return (
    <div className="card" style={{ padding: 16 }}>
      {/* image: self-closing + brace attributes */}
      <img src={product.imageUrl} alt={product.name} />

      <h2>{product.name}</h2>
      <p>Price: {product.price.toLocaleString()} won</p>

      {/* show stock with an expression (ternary) — more in the next chapter */}
      <p>{product.inStock ? 'In stock' : 'Out of stock'}</p>
    </div>
  );
}

export default ProductCard;

那个示例运用了全部三种结构规则(单一根节点、闭合标签、className),以及所有常见的大括号用法(文本插值、属性、算术运算、三元运算、样式对象)。

总结

  • JSX是JavaScript,而非HTML——正因如此其语法比HTML更为严格。
  • 三种结构规则:将兄弟元素包裹在同一个根节点中(使用Fragment即可),每个标签都必须闭合,属性名需采用驼峰式命名(class→className,for→htmlFor)。
  • 大括号{}用于包裹JavaScript表达式,无论是在文本中还是属性中。
  • 只有能生成值的表达式才能放在大括号内——诸如if或for这样的语句则不行。
  • {{ }}并非新语法,而是大括号内的对象。内联样式就是典型的例子。
  • 接下来的自然步骤是从外部向组件传递数据——即props。在组件内部硬编码product对象仅适用于演示;而通过props,同一个ProductCard就能渲染多个产品,这时TypeScript的类型系统才真正发挥重要作用。

    参考资料

    • React关于JSX标记的文档
    • React关于嵌入表达式的文档