首页 / React 19 入门教程 / JSX 本质

React 19 入门教程

JSX 本质

本教程共 50 篇 · 第 5 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

ReactJSXBabel语法扩展编译JavaScript

5. JSX 本质

本节目标:理解 JSX 不是 HTML,而是 JavaScript 语法扩展,掌握它的底层编译逻辑和核心规则。

JSX 到底是什么

JSX 是 JavaScript XML 的缩写——在 JavaScript 里写类似 HTML 的标签。

const element = <h1 className="title">Hello, React!</h1>;

这行代码既不是字符串,也不是 HTML。它是 JSX,一种语法糖。

JSX 会被编译器(Babel、esbuild、SWC 等)转译成普通的 JavaScript 函数调用:

// 编译前
const element = <h1 className="title">Hello, React!</h1>;

// 编译后(旧版转换)
const element = React.createElement(
  'h1',
  { className: 'title' },
  'Hello, React!'
);

// 编译后(React 17+ 新版自动转换)
import { jsx } from 'react/jsx-runtime';
const element = jsx(
  'h1',
  { className: 'title', children: 'Hello, React!' }
);
Note

React 17+ 用了新的 JSX 自动转换,不再需要手动 import React。底层逻辑没变——JSX 最终变成函数调用,只是调用目标从 React.createElement 变成了 jsx()

为什么要有 JSX

传统方式的问题

没有 JSX 之前,写 React 是这样的:

React.createElement('div', { className: 'card' },
  React.createElement('h2', null, '标题'),
  React.createElement('p', null, '内容'),
  React.createElement('button', { onClick: handleClick }, '点击')
);

嵌套一多,代码变成”回调地狱”,根本没法看。

JSX 的解法

JSX 把标签和逻辑放在一起,代码结构一目了然:

<div className="card">
  <h2>标题</h2>
  <p>内容</p>
  <button onClick={handleClick}>点击</button>
</div>

这就是 React 的核心设计理念:渲染逻辑和标签应该放在一起。按钮的外观和点击逻辑,写在同一个文件里,而不是分散在 HTML 和 JS 两个文件。

JSX 不是 HTML:四个关键区别

1. 只能返回一个根元素

JSX 看起来像 HTML,但本质是 JavaScript 表达式。一个表达式只能返回一个值。

// 错误:返回了多个根元素
function App() {
  return (
    <h1>标题</h1>
    <p>内容</p>
  );
}

正确做法——用父元素包裹:

function App() {
  return (
    <div>
      <h1>标题</h1>
      <p>内容</p>
    </div>
  );
}

或者用 Fragment(空标签),不产生额外 DOM 节点:

function App() {
  return (
    <>
      <h1>标题</h1>
      <p>内容</p>
    </>
  );
}
Tip

<>...</><React.Fragment>...</React.Fragment> 的简写。当你不想多加一个 <div> 时就用它。

2. 标签必须闭合

HTML 里 <img><br> 可以不闭合。JSX 不行。

// 错误
<img src="photo.jpg">
<br>

// 正确
<img src="photo.jpg" />
<br />

3. 属性名用驼峰式

HTML 属性 classtabindexonclick 在 JSX 里要写成:

HTMLJSX
classclassName
tabindextabIndex
onclickonClick
forhtmlFor
stroke-widthstrokeWidth

原因:JSX 编译后属性变成 JavaScript 对象的键,而 JS 变量名不能有 -class 又是保留字。

<img
  className="avatar"
  src="photo.jpg"
  alt="头像"
/>
Warning

aria-*data-* 属性保持原样,不用驼峰。比如 data-idaria-label

4. 用 {} 嵌入 JavaScript 表达式

JSX 里用花括号 {} 可以嵌入任何 JavaScript 表达式:

const name = 'React';
const isLoggedIn = true;

<h1>Hello, {name}</h1>          // 变量
<p>{1 + 1}</p>                   // 计算
<span>{isLoggedIn ? '已登录' : '未登录'}</span>  // 三元

这个后面第 8 节会详细讲。

JSX 是表达式

因为 JSX 编译后是函数调用,所以它可以用在任何表达式能出现的地方:

const element = <h1>Hello</h1>;

// 赋值给变量
const items = [<li>1</li>, <li>2</li>];

// 作为返回值
function Greeting({ isLoggedIn }) {
  if (isLoggedIn) {
    return <h1>欢迎回来</h1>;
  }
  return <h1>请登录</h1>;
}

JSX 防注入攻击

React 默认会转义 JSX 中的所有字符串,防止 XSS 攻击:

const userInput = '<img src=x onerror="alert(1)">';

// 安全:会被当作文本渲染,不会执行
<div>{userInput}</div>

如果你真的需要渲染 HTML 字符串,要用 dangerouslySetInnerHTML(名字就在提醒你危险):

<div dangerouslySetInnerHTML={{ __html: userInput }} />
Warning

尽量避免使用 dangerouslySetInnerHTML。用户输入的内容直接渲染等于开门揖盗。

本节小结

  • JSX 是 JavaScript 语法扩展,编译后变成 React.createElement() 调用
  • 四个关键区别:单根元素、标签闭合、驼峰属性、{} 嵌入表达式
  • <>...</> 是 Fragment,不产生额外 DOM
  • JSX 默认防注入,不要轻易用 dangerouslySetInnerHTML

下一节我们写第一个真正的 React 组件。