Skip to content
第 2 章 前端 ⏱ 12 分钟阅读

第 2 章:JSX 语法 ​

学习目标 ​

  • 理解 JSX 是 JavaScript 的语法扩展
  • 掌握 JSX 表达式、属性、子节点写法
  • 区分 JSX 与 HTML 的 4 个关键差异
  • 避开 JSX 的 5 个常见坑

一、JSX 是什么? ​

JSX = JavaScript + XML 语法。它让你在 JS 里直接写类似 HTML 的代码,最终被 Babel 编译成 React.createElement() 调用。

tsx
const element = <h1 className="title">Hello</h1>;
// 编译后:
// const element = React.createElement('h1', { className: 'title' }, 'Hello');

JSX 不是字符串、不是 HTML,是 JS 的语法糖。

二、JSX 表达式(插值) ​

用 {} 嵌入任意 JS 表达式:

tsx
function Greeting() {
	const name = 'React';
	const score = 95;

	return (
		<div>
			<p>你好,{name}</p>
			<p>分数:{score + 5}</p>
			<p>当前时间:{new Date().toLocaleString()}</p>
			<p>结果:{score >= 60 ? '及格' : '不及格'}</p>
		</div>
	);
}

可以放:

  • 变量、字面量
  • 三元、函数调用、对象属性
  • JSX 本身

不能放:

  • if/else 语句块
  • for 循环
  • 普通对象(除非是 React 元素)

三、属性写法 ​

tsx
function Avatar() {
	return (
		<img
			src="https://example.com/a.png"
			alt="头像"
			className="avatar" // ⚠️ 不是 class,是 className
			tabIndex={0} // ⚠️ 不是 tabindex,是 tabIndex
			style={{ width: 100, height: 100 }} // style 是对象
			onClick={() => alert('hi')} // 事件 onClick(驼峰)
		/>
	);
}

⚠️ 坑 1:JSX 更接近 HTML 属性 → class 改成 className,for 改成 htmlFor,tabindex 改成 tabIndex。 ⚠️ 坑 2:style 必须是对象。

四、条件渲染 ​

写法 1:三元运算符(最常用)

tsx
function Status({ isLogin }: { isLogin: boolean }) {
	return <div>{isLogin ? <p>已登录</p> : <p>请登录</p>}</div>;
}

写法 2:与运算符 &&(只关心 true 情况)

tsx
function Notice({ count }: { count: number }) {
	return <div>{count > 0 && <p>你有 {count} 条未读消息</p>}</div>;
}

写法 3:立即执行函数

tsx
function Panel({ mode }: { mode: 'a' | 'b' }) {
	return (
		<div>
			{(() => {
				if (mode === 'a') return <p>A 模式</p>;
				return <p>B 模式</p>;
			})()}
		</div>
	);
}

⚠️ 坑 3:count && <X /> 当 count = 0 时,会渲染数字 0 到页面上!必须写 count > 0 && <X />。

五、列表渲染 ​

用 map 把数组转成 JSX,必须设置 key:

tsx
function TodoList() {
	const todos = [
		{ id: 1, text: '学 React' },
		{ id: 2, text: '写组件' },
		{ id: 3, text: '部署上线' },
	];

	return (
		<ul>
			{todos.map((todo) => (
				<li key={todo.id}>{todo.text}</li> // �️ key 必须唯一且稳定
			))}
		</ul>
	);
}

⚠️ 坑 4:key 不要用数组 index,列表增删改时 index 会变,导致性能问题和组件状态错乱。用业务 ID。 ⚠️ 坑 5:每个 map 返回的 JSX 列表必须有 key,控制台会爆红警告。

六、Fragment 片段 ​

返回多个元素时,不想多包一层 div,用 <>...</> 或 <Fragment>:

tsx
import { Fragment } from 'react';

function TwoCols() {
	return (
		<>
			<h1>左</h1>
			<h1>右</h1>
		</>
	);
}

带 key 时必须用完整写法:

tsx
function List() {
	return (
		<Fragment>
			{items.map((item) => (
				<Fragment key={item.id}>
					<dt>{item.name}</dt>
					<dd>{item.desc}</dd>
				</Fragment>
			))}
		</Fragment>
	);
}

七、JSX 防注入 ​

React 默认对所有字符串插值做转义,<script>alert(1)</script> 这种字符串渲染出来是文本,不会执行。

tsx
const userInput = '<script>alert(1)</script>';
return <div>{userInput}</div>; // 安全,只显示字符串

但 dangerouslySetInnerHTML 会跳过转义,慎用:

tsx
<div dangerouslySetInnerHTML={{ __html: '<b>html</b>' }} />

八、本章小结 ​

要点关键
JSX 本质编译为 React.createElement
插值{} 内放 JS 表达式,不能放语句
属性className/htmlFor/驼峰事件
条件渲染三元 / && / 立即执行函数
列表渲染map + 唯一 key,不用 index
Fragment<> 避免多余 DOM 包裹
安全默认转义,除非显式 dangerouslySetInnerHTML

动手练习 ​

  1. 用户卡片:渲染 [{name, age}] 列表,大于 18 显示「成年」,否则「未成年」
  2. 状态徽章:写一个组件,根据 status: 'success'|'error'|'pending' 显示不同颜色 div
  3. 修复 bug:找出下面代码的 2 个坑并改对
    tsx
    {
    	items.map((item, i) => <li key={i}>{item}</li>);
    }
    {
    	count && <Badge count={count} />;
    }

下一章:第 3 章:组件与 Props →

本站基于 VitePress 构建 · 由 StackHub 团队维护