Skip to content

React JSX 语法详解

概述

JSX (JavaScript XML) 是 React 的核心特性之一,它是 JavaScript 的语法扩展,允许我们在 JavaScript 代码中编写类似 HTML 的标记。JSX 使得组件的结构更加直观和易于理解。

🎯 什么是 JSX?

JSX 基本概念

jsx
// JSX 示例
const element = <h1>Hello, React!</h1>;

// 这不是字符串,也不是 HTML
// 这是 JSX - JavaScript 的语法扩展

JSX 的本质

jsx
// JSX 代码
const element = <h1 className="greeting">Hello, world!</h1>;

// 编译后的 JavaScript 代码
const element = React.createElement(
    'h1',
    { className: 'greeting' },
    'Hello, world!'
);

// 最终生成的对象
const element = {
    type: 'h1',
    props: {
        className: 'greeting',
        children: 'Hello, world!'
    }
};

📝 JSX 基础语法

1. 嵌入表达式

在 JSX 中使用大括号 {} 嵌入 JavaScript 表达式:

jsx
function Greeting() {
    const name = '张三';
    const age = 25;
    
    return (
        <div>
            {/* 嵌入变量 */}
            <h1>你好,{name}!</h1>
            
            {/* 嵌入表达式 */}
            <p>明年你将 {age + 1} 岁</p>
            
            {/* 嵌入函数调用 */}
            <p>当前时间:{new Date().toLocaleTimeString()}</p>
            
            {/* 嵌入三元表达式 */}
            <p>{age >= 18 ? '成年人' : '未成年人'}</p>
        </div>
    );
}

2. JSX 属性

jsx
function ImageCard() {
    const imageUrl = 'https://example.com/image.jpg';
    const altText = '示例图片';
    const isActive = true;
    
    return (
        <div>
            {/* 字符串属性 */}
            <img src="logo.png" alt="Logo" />
            
            {/* 动态属性 */}
            <img src={imageUrl} alt={altText} />
            
            {/* 布尔属性 */}
            <button disabled={!isActive}>点击</button>
            
            {/* className 代替 class */}
            <div className="container">内容</div>
            
            {/* 驼峰命名 */}
            <label htmlFor="username">用户名</label>
            <input id="username" tabIndex="1" />
        </div>
    );
}

3. 子元素

jsx
function Layout() {
    return (
        <div className="layout">
            {/* 单个子元素 */}
            <header>
                <h1>网站标题</h1>
            </header>
            
            {/* 多个子元素 */}
            <main>
                <article>文章内容</article>
                <aside>侧边栏</aside>
            </main>
            
            {/* 嵌套子元素 */}
            <footer>
                <div>
                    <p>版权信息</p>
                    <p>联系方式</p>
                </div>
            </footer>
        </div>
    );
}

🔧 JSX 高级用法

1. 条件渲染

jsx
function UserGreeting({ isLoggedIn, username }) {
    return (
        <div>
            {/* 使用 && 运算符 */}
            {isLoggedIn && <p>欢迎回来,{username}!</p>}
            
            {/* 使用三元表达式 */}
            {isLoggedIn ? (
                <button>退出登录</button>
            ) : (
                <button>登录</button>
            )}
            
            {/* 使用立即执行函数 */}
            {(() => {
                if (isLoggedIn) {
                    return <p>已登录</p>;
                } else {
                    return <p>未登录</p>;
                }
            })()}
        </div>
    );
}

2. 列表渲染

jsx
function TodoList() {
    const todos = [
        { id: 1, text: '学习 React', completed: false },
        { id: 2, text: '编写代码', completed: true },
        { id: 3, text: '测试应用', completed: false }
    ];
    
    return (
        <ul>
            {todos.map(todo => (
                <li 
                    key={todo.id}
                    style={{
                        textDecoration: todo.completed ? 'line-through' : 'none'
                    }}
                >
                    {todo.text}
                </li>
            ))}
        </ul>
    );
}

3. 片段 (Fragments)

jsx
function Table() {
    return (
        <table>
            <tbody>
                {/* 使用 Fragment 避免额外的 DOM 节点 */}
                <React.Fragment>
                    <tr><td>行 1</td></tr>
                    <tr><td>行 2</td></tr>
                </React.Fragment>
                
                {/* 简写语法 */}
                <>
                    <tr><td>行 3</td></tr>
                    <tr><td>行 4</td></tr>
                </>
            </tbody>
        </table>
    );
}

4. 样式处理

jsx
function StyledComponent() {
    // 内联样式对象
    const containerStyle = {
        backgroundColor: '#f0f0f0',
        padding: '20px',
        borderRadius: '8px'
    };
    
    const isImportant = true;
    
    return (
        <div>
            {/* 内联样式 */}
            <div style={containerStyle}>
                <h2 style={{ color: 'blue', fontSize: '24px' }}>标题</h2>
            </div>
            
            {/* 动态 className */}
            <p className={`text ${isImportant ? 'important' : ''}`}>
                内容
            </p>
            
            {/* 使用模板字符串 */}
            <div className={`card ${isImportant && 'highlight'}`}>
                卡片内容
            </div>
        </div>
    );
}

🎨 JSX 中的特殊情况

1. 注释

jsx
function CommentExample() {
    return (
        <div>
            {/* 这是 JSX 中的注释 */}
            
            {/* 
                多行注释
                可以这样写
            */}
            
            <h1>标题</h1>
            
            {
                // 单行注释也可以
                // 但需要在大括号内
            }
        </div>
    );
}

2. 自闭合标签

jsx
function SelfClosingTags() {
    return (
        <div>
            {/* HTML 中的自闭合标签在 JSX 中必须闭合 */}
            <img src="image.jpg" alt="图片" />
            <input type="text" />
            <br />
            <hr />
            
            {/* 自定义组件也可以自闭合 */}
            <CustomComponent />
        </div>
    );
}

3. 保留字处理

jsx
function ReservedWords() {
    return (
        <div>
            {/* class -> className */}
            <div className="container"></div>
            
            {/* for -> htmlFor */}
            <label htmlFor="input">标签</label>
            <input id="input" />
            
            {/* 其他驼峰命名 */}
            <div tabIndex="0" onClick={() => {}}></div>
        </div>
    );
}

🔒 JSX 安全性

防止 XSS 攻击

jsx
function SafeComponent() {
    // 用户输入的内容
    const userInput = '<script>alert("XSS")</script>';
    
    return (
        <div>
            {/* React 会自动转义,防止 XSS */}
            <p>{userInput}</p>
            {/* 显示为:<script>alert("XSS")</script> */}
            
            {/* 如果确实需要渲染 HTML(不推荐) */}
            <div dangerouslySetInnerHTML={{ __html: userInput }} />
            {/* 这会执行脚本,非常危险! */}
        </div>
    );
}

📋 JSX 最佳实践

1. 保持 JSX 简洁

jsx
// ❌ 不好的做法
function BadExample() {
    return (
        <div>
            {users.filter(u => u.active).map(u => (
                <div key={u.id}>
                    {u.name} - {u.email} - {u.age > 18 ? '成年' : '未成年'}
                </div>
            ))}
        </div>
    );
}

// ✅ 好的做法
function GoodExample() {
    const activeUsers = users.filter(u => u.active);
    
    return (
        <div>
            {activeUsers.map(user => (
                <UserCard key={user.id} user={user} />
            ))}
        </div>
    );
}

function UserCard({ user }) {
    const ageStatus = user.age > 18 ? '成年' : '未成年';
    
    return (
        <div>
            {user.name} - {user.email} - {ageStatus}
        </div>
    );
}

2. 合理使用条件渲染

jsx
function ConditionalExample({ isLoading, error, data }) {
    // ✅ 提前返回
    if (isLoading) {
        return <LoadingSpinner />;
    }
    
    if (error) {
        return <ErrorMessage error={error} />;
    }
    
    if (!data) {
        return <EmptyState />;
    }
    
    return <DataDisplay data={data} />;
}

3. 使用 key 属性

jsx
function ListExample() {
    const items = [
        { id: 1, name: '项目 1' },
        { id: 2, name: '项目 2' },
        { id: 3, name: '项目 3' }
    ];
    
    return (
        <ul>
            {/* ✅ 使用唯一的 id 作为 key */}
            {items.map(item => (
                <li key={item.id}>{item.name}</li>
            ))}
            
            {/* ❌ 避免使用索引作为 key(除非列表是静态的) */}
            {items.map((item, index) => (
                <li key={index}>{item.name}</li>
            ))}
        </ul>
    );
}

🎯 实战示例

完整的表单组件

jsx
function ContactForm() {
    const [formData, setFormData] = useState({
        name: '',
        email: '',
        message: ''
    });
    const [errors, setErrors] = useState({});
    const [isSubmitting, setIsSubmitting] = useState(false);
    
    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData(prev => ({
            ...prev,
            [name]: value
        }));
    };
    
    const handleSubmit = (e) => {
        e.preventDefault();
        setIsSubmitting(true);
        
        // 表单验证和提交逻辑
        setTimeout(() => {
            setIsSubmitting(false);
            alert('表单已提交!');
        }, 1000);
    };
    
    return (
        <form onSubmit={handleSubmit} className="contact-form">
            <div className="form-group">
                <label htmlFor="name">姓名</label>
                <input
                    type="text"
                    id="name"
                    name="name"
                    value={formData.name}
                    onChange={handleChange}
                    required
                />
                {errors.name && (
                    <span className="error">{errors.name}</span>
                )}
            </div>
            
            <div className="form-group">
                <label htmlFor="email">邮箱</label>
                <input
                    type="email"
                    id="email"
                    name="email"
                    value={formData.email}
                    onChange={handleChange}
                    required
                />
                {errors.email && (
                    <span className="error">{errors.email}</span>
                )}
            </div>
            
            <div className="form-group">
                <label htmlFor="message">留言</label>
                <textarea
                    id="message"
                    name="message"
                    value={formData.message}
                    onChange={handleChange}
                    rows="4"
                    required
                />
                {errors.message && (
                    <span className="error">{errors.message}</span>
                )}
            </div>
            
            <button 
                type="submit" 
                disabled={isSubmitting}
                className={isSubmitting ? 'submitting' : ''}
            >
                {isSubmitting ? '提交中...' : '提交'}
            </button>
        </form>
    );
}

📝 本章小结

JSX 是 React 开发的核心,它让我们能够以声明式的方式描述 UI。掌握 JSX 语法是学习 React 的重要一步。

关键要点

  • ✅ JSX 是 JavaScript 的语法扩展,不是模板语言
  • ✅ 使用 {} 嵌入 JavaScript 表达式
  • ✅ JSX 属性使用驼峰命名法
  • ✅ React 自动转义内容,防止 XSS 攻击
  • ✅ 列表渲染时必须提供唯一的 key
  • ✅ 保持 JSX 简洁,提取复杂逻辑到组件或函数

下一步

在下一章中,我们将学习 React 组件的基础知识,了解如何创建和使用组件。


继续学习下一章 - React 组件基础