第四章:JSX 详解
4.1 什么是 JSX
JSX(JavaScript XML)是 React 中用于描述 UI 的语法扩展。它看起来像是 HTML,但实际上是 JavaScript。
const element = <h1>Hello, world!</h1>;
这段 JSX 代码会被 Babel 等工具转换为:
const element = React.createElement('h1', null, 'Hello, world!');
为什么要用 JSX:
- 直观:在 JavaScript 中直接描述 UI 结构
- 强大:可以使用 JavaScript 的全部能力
- 类型安全:编译时就能发现错误
4.2 JSX 语法规则
必须有一个根元素:
JSX 表达式必须有一个外层包裹元素。
// 正确 return ( <div> <h1>标题</h1> <p>内容</p> </div> ); // 错误 - 没有根元素 return ( <h1>标题</h1> <p>内容</p> ); // 使用 Fragment 作为根元素 return ( <> <h1>标题</h1> <p>内容</p> </> );
使用 className 代替 class:
class 是 JavaScript 的保留字,所以在 JSX 中使用 className。
// 正确 <div className="container">内容</div> // 错误 <div class="container">内容</div>
使用 camelCase 属性名:
HTML 属性在 JSX 中使用 camelCase。
// HTML <input readonly tabindex="0" /> // JSX <input readOnly tabIndex={0} />
标签必须闭合:
所有标签都必须正确闭合。
// 正确 <img src="photo.jpg" alt="照片" /> <input type="text" /> // 错误 <img src="photo.jpg" alt="照片"> <input type="text">
4.3 在 JSX 中嵌入表达式
使用花括号 {} 在 JSX 中嵌入 JavaScript 表达式。
变量和表达式:
const name = 'React'; const element = <h1>Hello, {name}!</h1>; const sum = 1 + 2; const result = <p>1 + 2 = {sum}</p>; const element = <h1>2 + 2 = {2 + 2}</h1>;
函数调用:
function formatName(user) { return user.firstName + ' ' + user.lastName; } const user = { firstName: '张', lastName: '三' }; const element = <h1>{formatName(user)}</h1>;
条件渲染:
const isLoggedIn = true; // 使用三元运算符 const element = ( <div> {isLoggedIn ? <UserGreeting /> : <GuestGreeting />} </div> ); // 使用逻辑与运算符 const element = ( <div> {isLoggedIn && <UserGreeting />} </div> ); // 使用变量 let content; if (isLoggedIn) { content = <UserGreeting />; } else { content = <GuestGreeting />; } const element = <div>{content}</div>;
4.4 JSX 中的属性
字符串字面量:
const element = <div tabIndex="0"></div>; const element = <img src="photo.jpg" />;
嵌入表达式:
const avatarUrl = 'https://example.com/avatar.jpg'; const element = <img src={avatarUrl} />; const element = <div tabIndex={0}></div>;
展开属性:
const props = { firstName: '张', lastName: '三', age: 25 }; const element = <User {...props} />; // 等同于 const element = <User firstName="张" lastName="三" age={25} />;
4.5 JSX 防止 XSS 攻击
React DOM 在渲染之前会转义所有嵌入的值。这确保了你永远不会注入未转义的内容。
const title = response.potentiallyMaliciousInput; // 这是安全的 const element = <h1>{title}</h1>;
默认情况下,React DOM 会将所有内容在渲染前进行转义。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(跨站脚本)攻击。
危险地设置 HTML:
有时你需要设置 HTML 内容(如富文本编辑器),这时可以使用 dangerouslySetInnerHTML:
function MyComponent() { const htmlContent = { __html: '<b>粗体文本</b>' }; return <div dangerouslySetInnerHTML={htmlContent} />; }
⚠️ 警告:使用 dangerouslySetInnerHTML 存在安全风险,确保内容是可信的。
4.6 JSX 表示对象
Babel 会将 JSX 编译为 React.createElement() 调用。
这两个例子完全等同:
const element = ( <h1 className="greeting"> Hello, world! </h1> ); const element = React.createElement( 'h1', { className: 'greeting' }, 'Hello, world!' );
React.createElement() 执行后返回一个类似这样的对象:
const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } };
这样的对象被称为“React 元素”,它描述了你希望在屏幕上看到的内容。React 读取这些对象,用它们来构建 DOM 并保持其更新。
4.7 注释
在 JSX 中使用注释:
const element = ( <div> {/* 这是注释 */} <h1>标题</h1> {/ 多行 注释 /} </div> );
4.8 JSX 的 children
字符串:
const element = <div>Hello World</div>;
JSX 元素:
const element = ( <div> <span>Hello</span> <span>World</span> </div> );
混合类型数组:
const items = ['苹果', '香蕉', '橙子']; const element = ( <ul> {>items.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> );
函数作为 children:
function Repeat(props) { let items = []; for (let i = 0; i < props.numTimes; i++) { items.push(props.children(i)); } return <div>{items}</div>; } function ListOfTenThings() { return ( <Repeat numTimes={10}> {(index) => <div key={index}>这是第 {index} 项</div>} </Repeat> ); }
4.9 布尔值、Null 和 Undefined
false、null、undefined 和 true 都是有效的 children,但它们不会被渲染。
// 以下都会渲染相同的结果 <div /> <div></div> <div>{false}</div> <div>{null}</div> <div>{undefined}</div> <div>{true}</div>
这在条件渲染中很有用:
<div> {showHeader && <Header />} </div>
注意:数字 0 会被渲染出来,要注意这种情况:
// 如果 items.length 为 0,会显示 0 <div>{items.length && <MessageList items={items} />}</div> // 正确做法 <div>{items.length > 0 && <MessageList items={items} />}</div>
4.10 Fragment
Fragment 允许你将子元素列表分组,而无需向 DOM 添加额外节点。
import React from 'react'; function Example() { return ( <React.Fragment> <ChildA /> <ChildB /> <ChildC /> </React.Fragment> ); } // 短语法 function Example() { return ( <> <ChildA /> <ChildB /> <ChildC /> </> ); }
带 key 的 Fragment:
function Glossary(props) { return ( <dl> {props.items.map(item => ( <React.Fragment key={item.id}> <dt>{item.term}</dt> <dd>{item.description}</dd> </React.Fragment> ))} </dl> ); }
4.11 总结
本章深入讲解了 JSX 的各个方面:
- JSX 的基本语法规则
- 嵌入表达式
- 属性设置
- XSS 防护
- children 的处理
- Fragment 的使用
掌握 JSX 是 React 开发的基础。下一章我们将学习组件的概念和用法。