JSX 本质
本教程共 50 篇 · 第 5 篇 · 更新于 2026-07-29 · 约 7 分钟阅读
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!' }
);
NoteReact 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 属性 class、tabindex、onclick 在 JSX 里要写成:
| HTML | JSX |
|---|---|
class | className |
tabindex | tabIndex |
onclick | onClick |
for | htmlFor |
stroke-width | strokeWidth |
原因:JSX 编译后属性变成 JavaScript 对象的键,而 JS 变量名不能有 -,class 又是保留字。
<img
className="avatar"
src="photo.jpg"
alt="头像"
/>
Warning
aria-*和data-*属性保持原样,不用驼峰。比如data-id、aria-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 组件。