react---JSX完全指南:从基础语法到进阶实战

JSX(JavaScript XML)是 React 生态中最具辨识度的特性之一,它将类 HTML 的语法嵌入 JavaScript 中,让开发者能够以直观的方式编写 UI 结构,同时保留 JavaScript 的逻辑能力。很多开发者最初会将 JSX 误认为是 "HTML 在 JS 中的变体",但实际上它是 JavaScript 的语法糖,最终会被编译为普通的 JavaScript 函数调用。本文将从本质、基础语法、进阶用法、常见误区四个维度,全面解析 JSX 的使用方法,帮助你彻底掌握这一核心技能。

一、JSX 是什么?------ 不止是 "HTML+JS"

1. JSX 的本质:语法糖

JSX 是 Facebook 为 React 开发的一种语法扩展,其核心作用是简化 React 元素的创建 。当我们编写 JSX 代码时,Babel(或 TypeScript)会将其编译为 React 的createElement函数调用(React 17 + 也支持更简洁的jsx/jsxs函数)。

举个例子

javascript 复制代码
// 我们编写的JSX代码
const element = <h1 className="title">Hello, JSX!</h1>;

编译后的 JavaScript 代码(React 17 之前):

arduino 复制代码
const element = React.createElement(
  'h1', // 元素类型
  { className: 'title' }, // 元素属性
  'Hello, JSX!' // 子元素
);

React 17 + 的编译结果(无需显式引入 React):

php 复制代码
import { jsx as _jsx } from 'react/jsx-runtime';
const element = _jsx('h1', {
  className: 'title',
  children: 'Hello, JSX!'
});

从编译结果可以看出:JSX 最终会被转换为描述 UI 的 JavaScript 对象(React 元素) ,而不是直接渲染为 DOM 节点。这也是 JSX 能够与 JavaScript 逻辑无缝结合的根本原因。

2. 为什么要用 JSX?

在 JSX 出现之前,开发者需要通过React.createElement手动创建 UI 元素,代码冗长且可读性差。JSX 的出现解决了以下问题:

  • 直观性:类 HTML 的语法让 UI 结构一目了然,比纯 JavaScript 代码更易读、易维护;
  • 无缝集成逻辑:可以在 JSX 中直接嵌入 JavaScript 表达式,实现 UI 与业务逻辑的紧密结合;
  • 编译时检查:Babel 和 TypeScript 会在编译阶段检查 JSX 的语法错误,提前规避运行时问题;
  • 组件化支持:JSX 天然支持 React 组件的嵌套和组合,是 React 组件化思想的核心载体。

注意:JSX 并非 React 的强制要求,你可以始终使用React.createElement编写代码,但几乎所有 React 项目都会选择 JSX 以提升开发效率。

二、JSX 的核心语法规则:必掌握的基础

JSX 虽然看起来像 HTML,但本质是 JavaScript,因此有一套自己的语法规则。以下是最核心的规则,也是新手最容易踩坑的地方。

1. 标签必须闭合

与 HTML 不同,JSX 要求所有标签必须显式闭合,包括单标签(如<input><img>)。

ini 复制代码
// 错误:标签未闭合
const input = <input type="text">;
const img = <img src="logo.png">;

// 正确:单标签使用自闭合语法
const input = <input type="text" />;
const img = <img src="logo.png" alt="logo" />;

// 双标签必须成对出现
const div = <div>Hello, JSX</div>;

2. 只能有一个根元素

JSX 表达式中不能直接返回多个同级元素,必须用一个根元素包裹(或使用 Fragment 片段)。

javascript 复制代码
// 错误:多个根元素
const App = () => {
  return (
    <h1>标题</h1>
    <p>内容</p>
  );
};

// 正确:用div作为根元素
const App = () => {
  return (
    <div>
      <h1>标题</h1>
      <p>内容</p>
    </div>
  );
};

3. 类名使用className而非class

在 JavaScript 中,class是关键字,因此 JSX 中不能使用class属性定义 CSS 类名,而是使用className(对应 DOM 的className属性)。

javascript 复制代码
// 错误:使用class关键字
const element = <div class="container">Hello</div>;

// 正确:使用className
const element = <div className="container">Hello</div>;

补充:在 React Native 中,类名使用style属性,而不是className

4. 表单标签的for属性改为htmlFor

同理,for是 JavaScript 的关键字,JSX 中使用htmlFor替代<label>标签的for属性。

ini 复制代码
// 错误:使用for关键字
const label = <label for="username">用户名:</label>;

// 正确:使用htmlFor
const label = <label htmlFor="username">用户名:</label>;
<input id="username" type="text" />;

5. 内联样式是对象形式

JSX 中的内联样式不能直接写 CSS 字符串,而是需要传递一个样式对象 ,属性名采用驼峰命名法(如fontSize而非font-size)。

javascript 复制代码
// 错误:CSS字符串形式
const element = <div style="font-size: 16px; color: red;">Hello</div>;

// 正确:样式对象形式
const element = <div style={{ fontSize: '16px', color: 'red' }}>Hello</div>;

// 推荐:将样式抽离为变量
const textStyle = {
  fontSize: '16px',
  color: 'red',
  marginTop: '10px' // 驼峰命名法
};
const element = <div style={textStyle}>Hello</div>;

6. 插入 JavaScript 表达式:使用{}

这是 JSX 最强大的特性之一:可以通过大括号{}在 JSX 中嵌入任意有效的 JavaScript 表达式(注意:是表达式,不是语句)。

ini 复制代码
// 1. 变量
const name = 'React';
const element = <h1>Hello, {name}!</h1>;

// 2. 算术运算
const a = 10;
const b = 20;
const element = <p>10 + 20 = {a + b}</p>;

// 3. 函数调用
const getGreeting = (name) => `Hello, ${name}!`;
const element = <h1>{getGreeting('JSX')}</h1>;

// 4. 三元运算符(条件表达式)
const isLogin = true;
const element = <p>{isLogin ? '已登录' : '请登录'}</p>;

// 5. 数组(会自动展开)
const list = ['苹果', '香蕉', '橙子'];
const element = <div>{list}</div>; // 渲染为:<div>苹果香蕉橙子</div>

注意:{}中只能放表达式 (有返回值的代码),不能放语句(如 if、for、switch 等)。如果需要使用语句,需在 JSX 外部处理。

7. JSX 中的注释

JSX 中的注释需要写在{}内,格式为/* 注释内容 */(单行注释也可以用//,但需要注意换行)。

css 复制代码
const element = (
  <div>
    {/* 这是JSX中的多行注释 */}
    <h1>Hello, JSX!</h1>
    {/* 单行注释也可以这样写 */}
    {/*
      多行注释
      可以换行
    */}
    <p>{/* 行内注释 */}这是内容</p>
  </div>
);

// 单行注释的另一种写法(注意换行)
const element = (
  <div>
    {/* 推荐 */}
    <h1>Hello, JSX!</h1>
    // 这种写法会报错,因为//不在{}内
    <p>{// 这种写法可行,但需要换行
      '内容'}</p>
  </div>
);

三、JSX 的进阶用法:从基础到实战

掌握了基础语法后,我们来看看 JSX 在实际开发中的高频进阶用法。

1. 片段(Fragment):避免多余的根节点

前面提到 JSX 必须有一个根元素,但有时我们不想添加额外的<div>等节点(避免 DOM 层级过深),此时可以使用React Fragment(片段),它会在渲染时被忽略,只保留子元素。

用法 1:<React.Fragment>

javascript 复制代码
import React from 'react';

const App = () => {
  return (
    <React.Fragment>
      <h1>标题</h1>
      <p>内容</p>
      <button>按钮</button>
    </React.Fragment>
  );
};

用法 2:空标签<> </>(简写形式)

这是 React 16.2 + 支持的简写语法,功能与<React.Fragment>一致,但不支持添加属性(如 key)。

javascript 复制代码
const App = () => {
  return (
    <>
      <h1>标题</h1>
      <p>内容</p>
      <button>按钮</button>
    </>
  );
};

用法 3:带 key 的 Fragment(仅支持完整写法)

当在列表中渲染 Fragment 时,需要为其添加 key 属性,此时必须使用完整的<React.Fragment>

javascript 复制代码
const list = [
  { id: 1, text: '第一项' },
  { id: 2, text: '第二项' }
];

const App = () => {
  return (
    <div>
      {list.map(item => (
        <React.Fragment key={item.id}>
          <p>{item.text}</p>
          <hr />
        </React.Fragment>
      ))}
    </div>
  );
};

2. 列表渲染:使用map并添加key

在 JSX 中渲染列表(如数组)时,通常使用Array.prototype.map方法,且必须为每个列表项添加唯一的key属性

ini 复制代码
const todos = [
  { id: 1, text: '学习JSX' },
  { id: 2, text: '学习React' },
  { id: 3, text: '开发项目' }
];

const TodoList = () => {
  return (
    <ul>
      {todos.map(todo => (
        // 正确:使用唯一的id作为key
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
};

关于key的重要注意事项:

  • key的作用:帮助 React 识别列表中元素的变化(添加、删除、排序),从而优化渲染性能;
  • key必须是唯一的:在同一列表中,每个元素的 key 不能重复;
  • 不要使用索引作为 key:如果列表的顺序发生变化(如排序、删除),索引会重新分配,导致 React 误判元素变化,引发性能问题或渲染错误;
  • key只在列表内部有效:key 是给 React 看的,不会传递给组件,因此不能在组件内部通过props.key获取。

3. 条件渲染:多种实现方式

在 JSX 中实现条件渲染有多种方式,可根据场景选择:

方式 1:三元运算符(适合简单条件)

javascript 复制代码
const isLogin = true;

const UserInfo = () => {
  return (
    <div>
      {isLogin ? (
        <p>欢迎回来,用户!</p>
      ) : (
        <button>请登录</button>
      )}
    </div>
  );
};

方式 2:逻辑与运算符&&(适合 "存在即渲染" 的场景)

ini 复制代码
const hasUnreadMsg = true;
const unreadCount = 5;

const MsgTip = () => {
  return (
    <div>
      {/* 当hasUnreadMsg为true时,渲染后面的元素;为false时,返回false,不渲染 */}
      {hasUnreadMsg && <span className="badge">{unreadCount}</span>}
    </div>
  );
};

方式 3:外部条件语句(适合复杂条件)

ini 复制代码
const UserRole = ({ role }) => {
  // 外部定义渲染逻辑
  let content;
  if (role === 'admin') {
    content = <p>管理员</p>;
  } else if (role === 'user') {
    content = <p>普通用户</p>;
  } else {
    content = <p>游客</p>;
  }

  return <div>{content}</div>;
};

方式 4:组件提取(适合极复杂的条件)

将不同条件的渲染逻辑提取为独立组件,让代码更清晰。

javascript 复制代码
const AdminPanel = () => <p>管理员面板</p>;
const UserPanel = () => <p>用户面板</p>;
const GuestPanel = () => <p>游客面板</p>;

const Panel = ({ role }) => {
  switch (role) {
    case 'admin':
      return <AdminPanel />;
    case 'user':
      return <UserPanel />;
    default:
      return <GuestPanel />;
  }
};

4. 自定义组件的渲染:首字母大写

在 JSX 中渲染自定义 React 组件时,组件名必须以大写字母开头(这是 React 的约定,用于区分原生 HTML 标签)。

javascript 复制代码
// 正确:组件名首字母大写
const Button = () => <button>自定义按钮</button>;

const App = () => {
  return (
    <div>
      <Button /> {/* 渲染自定义组件 */}
      <button>原生按钮</button> {/* 渲染原生HTML标签 */}
    </div>
  );
};

// 错误:组件名小写,React会将其视为原生HTML标签(不存在的标签会渲染为<div>或报错)
const button = () => <button>自定义按钮</button>;
const App = () => {
  return <button />; // 渲染原生<button>,而非自定义组件
};

5. 属性传递(Props):向组件传递数据

可以通过 JSX 的属性(props)向自定义组件传递数据,属性名同样采用驼峰命名法(如onClickdataId)。

javascript 复制代码
// 子组件接收props
const Greeting = (props) => {
  return <h1>Hello, {props.name}!</h1>;
};

// 父组件传递props
const App = () => {
  return (
    <div>
      {/* 传递字符串属性 */}
      <Greeting name="React" />
      {/* 传递非字符串属性(需用{}包裹) */}
      <Greeting name={123} />
      {/* 传递布尔值 */}
      <Greeting isShow={true} />
      {/* 传递函数 */}
      <Greeting onButtonClick={() => alert('点击了')} />
      {/* 传递JSX元素(子元素,对应props.children) */}
      <Greeting>
        <p>这是子元素</p>
      </Greeting>
    </div>
  );
};

补充:props.children是一个特殊的 props,用于接收组件的子元素(如上面的<p>这是子元素</p>)。

6. 危险的 HTML 渲染:dangerouslySetInnerHTML

默认情况下,React 会转义 JSX 中的所有内容,防止 XSS 攻击(跨站脚本攻击)。但有时我们需要渲染原始的 HTML 字符串(如后端返回的富文本),此时可以使用dangerouslySetInnerHTML属性(注意:使用该属性存在安全风险,需确保内容是可信的)。

css 复制代码
// 原始HTML字符串
const htmlContent = '<p style="color: red;">这是富文本内容</p>';

// 错误:React会转义HTML标签,渲染为纯文本
const element = <div>{htmlContent}</div>;

// 正确:使用dangerouslySetInnerHTML渲染原始HTML
const element = <div dangerouslySetInnerHTML={{ __html: htmlContent }} />;

警告:不要将用户输入的内容直接通过 dangerouslySetInnerHTML 渲染,否则可能导致 XSS 攻击。如果必须渲染用户输入,需先进行 HTML 转义或过滤。

7. JSX 作为变量、返回值和参数

由于 JSX 最终会被编译为 JavaScript 对象,因此它可以作为变量存储、作为函数返回值、作为参数传递给函数。

javascript 复制代码
// 1. 作为变量
const header = <h1>Hello, JSX</h1>;

// 2. 作为函数返回值
const getHeader = () => {
  return <h1>Hello, JSX</h1>;
};

// 3. 作为参数传递
const renderElement = (element) => {
  return <div>{element}</div>;
};

const App = () => {
  return renderElement(header);
};

四、JSX 的常见误区与避坑指南

即使是有经验的开发者,也可能在使用 JSX 时踩坑。以下是最常见的误区及解决方案:

误区 1:混淆 HTML 和 JSX 的语法差异

问题 :使用classforstyle等 HTML 属性,导致语法错误或样式不生效。解决方案:牢记 JSX 的属性替换规则:

  • classclassName
  • forhtmlFor
  • style → 驼峰命名的样式对象
  • 自定义属性:使用data-*前缀(如data-id),React 会保留这些属性。

误区 2:在{}中使用语句(而非表达式)

问题 :在 JSX 的{}中写入 if、for、switch 等语句,导致编译错误。

jsx

kotlin 复制代码
// 错误:if是语句,不能放在{}内
const element = <div>{if (true) { return 'Hello' }}</div>;

解决方案:将语句移到 JSX 外部,或使用三元运算符、逻辑与等表达式替代。

误区 3:列表渲染忘记加key或使用索引作为key

问题 :列表渲染时未添加key,控制台出现警告;或使用索引作为key,导致列表排序 / 删除时渲染异常。解决方案 :使用唯一的 ID(如后端返回的 id、UUID)作为key;如果确实没有唯一 ID,可考虑生成唯一标识(如item.name + item.index),但尽量避免使用索引。

误区 4:过度使用dangerouslySetInnerHTML

问题 :随意使用dangerouslySetInnerHTML渲染不可信内容,导致 XSS 攻击风险。解决方案

  • 尽量避免使用dangerouslySetInnerHTML
  • 如果必须使用,确保内容是可信的(如后端自己生成的富文本);
  • 对用户输入的内容进行 HTML 转义(如使用he库)。

误区 5:忽略 JSX 的大小写敏感

问题 :将原生 HTML 标签大写(如<Div>),或自定义组件小写(如<button>),导致渲染错误。解决方案

  • 原生 HTML 标签:全小写(如<div><button>);
  • 自定义组件:首字母大写(如<Button><TodoList>)。

误区 6:直接修改propsstate后渲染 JSX

问题 :修改propsstate的原始值(如数组的push、对象的属性赋值),导致 React 无法检测到变化,JSX 不更新。解决方案 :遵循 React 的不可变原则,创建新的数组 / 对象(如使用concatmapspread运算符)。

五、JSX 的优势:为什么它能成为 React 的标配?

总结一下,JSX 之所以能成为 React 开发的核心工具,主要有以下优势:

  1. 直观性:类 HTML 的语法让 UI 结构与代码逻辑分离但又紧密结合,比纯 JavaScript 更易读;
  2. 灵活性:可以嵌入任意 JavaScript 表达式,实现复杂的逻辑渲染;
  3. 安全性:默认转义内容,防止 XSS 攻击;
  4. 组件化:天然支持 React 的组件化思想,便于复用和维护;
  5. 跨平台:不仅可以用于 Web 端的 DOM 渲染,还可以用于 React Native 的原生组件渲染(语法一致,底层渲染不同);
  6. 工具支持:Babel、TypeScript、ESLint 等工具对 JSX 有完善的支持,提升开发效率。

六、总结

JSX 是 React 开发的基础,它不是 HTML,也不是新的编程语言,而是 JavaScript 的语法糖。掌握 JSX 的核心语法规则(如标签闭合、className、表达式插入)、进阶用法(如 Fragment、列表渲染、条件渲染)和避坑指南,是编写高效、可维护的 React 代码的关键。

值得一提的是,JSX 并非 React 的专属特性,Vue 3 也支持 JSX 语法,甚至一些其他前端框架也开始兼容 JSX。因此,学好 JSX 不仅能提升 React 开发能力,也是前端工程师的通用技能。

最后,记住:JSX 的本质是 JavaScript,所有 JavaScript 的特性都可以与 JSX 结合使用。不要被类 HTML 的语法迷惑,始终以 JavaScript 的思维来编写 JSX。

相关推荐
光影少年14 小时前
React18 函数组件执行顺序、严格模式下重复执行问题
前端·javascript·react.js
whinc1 天前
React技术周刊 2026年第18周
react.js
whinc1 天前
React技术周刊 2026年第14周
react.js
whinc1 天前
React技术周刊 2026年第15周
react.js
whinc1 天前
React技术周刊 2026年第16周
react.js