Skip to content

jsx.jsx

文件信息

  • 📄 原文件:01_jsx.jsx
  • 🔤 语言:jsx

React JSX 基础 JSX 是 JavaScript 的语法扩展,用于描述 UI 结构。 它看起来像 HTML,但实际上会被编译成 JavaScript。

完整代码

jsx
/**
 * ============================================================
 *                    React JSX 基础
 * ============================================================
 * JSX 是 JavaScript 的语法扩展,用于描述 UI 结构。
 * 它看起来像 HTML,但实际上会被编译成 JavaScript。
 * ============================================================
 */

import React from 'react';

// ============================================================
//                    1. JSX 基础语法
// ============================================================

/**
 * 【什么是 JSX】
 *
 * JSX = JavaScript + XML
 * - 不是模板语言,而是 JavaScript 的语法扩展
 * - 会被 Babel 编译成 React.createElement() 调用
 * - 可以在 JSX 中使用任意 JavaScript 表达式
 *
 * 【编译示例】
 * JSX:
 *   <div className="hello">Hello</div>
 *
 * 编译后:
 *   React.createElement('div', { className: 'hello' }, 'Hello')
 */

// --- 基本元素 ---
function BasicElement() {
    // 单个元素
    return <h1>Hello, React!</h1>;
}

// --- 嵌套元素 ---
function NestedElements() {
    // 多个元素必须有一个父元素包裹
    return (
        <div>
            <h1>标题</h1>
            <p>段落内容</p>
        </div>
    );
}

// --- Fragment 片段 ---
function FragmentExample() {
    // 使用 Fragment 避免额外的 DOM 节点
    return (
        <>
            <h1>标题</h1>
            <p>段落内容</p>
        </>
    );

    // 或者使用完整写法(可以添加 key)
    // return (
    //     <React.Fragment key="unique">
    //         <h1>标题</h1>
    //         <p>段落内容</p>
    //     </React.Fragment>
    // );
}


// ============================================================
//                    2. JSX 中使用 JavaScript
// ============================================================

/**
 * 【在 JSX 中嵌入表达式】
 *
 * 使用花括号 {} 可以在 JSX 中嵌入任意 JavaScript 表达式
 * - 变量
 * - 函数调用
 * - 算术运算
 * - 三元表达式
 * - 数组方法
 */

// --- 变量和表达式 ---
function VariablesAndExpressions() {
    const name = 'Alice';
    const age = 25;
    const isAdult = age >= 18;

    return (
        <div>
            {/* 变量 */}
            <p>姓名: {name}</p>

            {/* 表达式 */}
            <p>年龄: {age}</p>
            <p>明年年龄: {age + 1}</p>

            {/* 函数调用 */}
            <p>大写姓名: {name.toUpperCase()}</p>

            {/* 三元表达式 */}
            <p>状态: {isAdult ? '成年' : '未成年'}</p>

            {/* 模板字符串 */}
            <p>{`${name} 今年 ${age} 岁`}</p>
        </div>
    );
}

// --- 条件渲染 ---
function ConditionalRendering() {
    const isLoggedIn = true;
    const user = { name: 'Alice', role: 'admin' };
    const messages = ['消息1', '消息2', '消息3'];

    return (
        <div>
            {/* 三元表达式 */}
            {isLoggedIn ? <p>欢迎回来!</p> : <p>请登录</p>}

            {/* && 短路运算 - 只渲染真值 */}
            {isLoggedIn && <p>你已登录</p>}

            {/* 多条件判断 */}
            {user.role === 'admin' && <button>管理面板</button>}
            {user.role === 'user' && <button>用户中心</button>}

            {/* 显示消息数量 */}
            {messages.length > 0 && (
                <p>你有 {messages.length} 条新消息</p>
            )}
        </div>
    );
}

// --- 复杂条件渲染 ---
function ComplexConditional() {
    const status = 'loading'; // 'loading' | 'success' | 'error'

    // 使用函数处理复杂条件
    const renderContent = () => {
        switch (status) {
            case 'loading':
                return <p>加载中...</p>;
            case 'success':
                return <p>加载成功!</p>;
            case 'error':
                return <p>加载失败</p>;
            default:
                return null;
        }
    };

    return (
        <div>
            {renderContent()}
        </div>
    );
}


// ============================================================
//                    3. 列表渲染
// ============================================================

/**
 * 【列表渲染】
 *
 * 使用数组的 map() 方法将数据数组转换为 JSX 元素数组
 *
 * 【key 的作用】
 * - 帮助 React 识别哪些元素改变了
 * - key 应该是稳定、唯一的标识符
 * - 不推荐使用数组索引作为 key(除非列表不会重新排序)
 */

// --- 基本列表渲染 ---
function BasicList() {
    const fruits = ['苹果', '香蕉', '橙子', '葡萄'];

    return (
        <ul>
            {fruits.map((fruit, index) => (
                // 简单列表可以用索引作为 key
                <li key={index}>{fruit}</li>
            ))}
        </ul>
    );
}

// --- 对象列表渲染 ---
function ObjectList() {
    const users = [
        { id: 1, name: 'Alice', email: 'alice@example.com' },
        { id: 2, name: 'Bob', email: 'bob@example.com' },
        { id: 3, name: 'Charlie', email: 'charlie@example.com' },
    ];

    return (
        <div>
            <h2>用户列表</h2>
            <ul>
                {users.map(user => (
                    // 使用唯一 id 作为 key
                    <li key={user.id}>
                        <strong>{user.name}</strong>
                        <span> - {user.email}</span>
                    </li>
                ))}
            </ul>
        </div>
    );
}

// --- 嵌套列表渲染 ---
function NestedList() {
    const categories = [
        {
            id: 1,
            name: '电子产品',
            items: ['手机', '电脑', '平板'],
        },
        {
            id: 2,
            name: '服装',
            items: ['T恤', '牛仔裤', '外套'],
        },
    ];

    return (
        <div>
            {categories.map(category => (
                <div key={category.id}>
                    <h3>{category.name}</h3>
                    <ul>
                        {category.items.map((item, index) => (
                            <li key={`${category.id}-${index}`}>{item}</li>
                        ))}
                    </ul>
                </div>
            ))}
        </div>
    );
}

// --- 过滤和排序列表 ---
function FilteredList() {
    const products = [
        { id: 1, name: 'iPhone', price: 999, inStock: true },
        { id: 2, name: 'MacBook', price: 1999, inStock: false },
        { id: 3, name: 'iPad', price: 799, inStock: true },
        { id: 4, name: 'AirPods', price: 199, inStock: true },
    ];

    // 过滤有库存的商品
    const inStockProducts = products.filter(p => p.inStock);

    // 按价格排序
    const sortedProducts = [...products].sort((a, b) => a.price - b.price);

    return (
        <div>
            <h3>有库存商品</h3>
            <ul>
                {inStockProducts.map(product => (
                    <li key={product.id}>
                        {product.name} - ${product.price}
                    </li>
                ))}
            </ul>

            <h3>按价格排序</h3>
            <ul>
                {sortedProducts.map(product => (
                    <li key={product.id}>
                        {product.name} - ${product.price}
                    </li>
                ))}
            </ul>
        </div>
    );
}


// ============================================================
//                    4. JSX 属性
// ============================================================

/**
 * 【JSX 属性规则】
 *
 * 1. 使用 camelCase 命名
 *    - class → className
 *    - for → htmlFor
 *    - tabindex → tabIndex
 *
 * 2. 布尔属性
 *    - disabled={true} 可简写为 disabled
 *    - 显式传 false: disabled={false}
 *
 * 3. 展开属性
 *    - {...props} 将对象的所有属性展开
 */

// --- className 和 style ---
function StylingExample() {
    const isActive = true;
    const customStyle = {
        color: 'blue',
        fontSize: '18px',      // 注意: camelCase
        backgroundColor: '#f0f0f0',
        padding: '10px',
    };

    return (
        <div>
            {/* className */}
            <p className="text-bold">粗体文字</p>

            {/* 动态 className */}
            <p className={isActive ? 'active' : 'inactive'}>
                动态类名
            </p>

            {/* 多个类名 */}
            <p className={`base-class ${isActive ? 'active' : ''}`}>
                多个类名
            </p>

            {/* 内联样式(对象形式) */}
            <p style={customStyle}>内联样式</p>

            {/* 直接写内联样式 */}
            <p style={{ color: 'red', fontWeight: 'bold' }}>
                直接内联样式
            </p>
        </div>
    );
}

// --- 属性展开 ---
function PropsSpread() {
    const buttonProps = {
        type: 'submit',
        className: 'btn btn-primary',
        disabled: false,
        onClick: () => console.log('clicked'),
    };

    const inputProps = {
        type: 'text',
        placeholder: '请输入...',
        maxLength: 100,
    };

    return (
        <div>
            {/* 展开所有属性 */}
            <button {...buttonProps}>提交</button>

            {/* 展开并覆盖某些属性 */}
            <button {...buttonProps} disabled={true}>
                禁用按钮
            </button>

            {/* 输入框 */}
            <input {...inputProps} />
        </div>
    );
}

// --- 特殊属性 ---
function SpecialAttributes() {
    return (
        <div>
            {/* htmlFor 代替 for */}
            <label htmlFor="username">用户名:</label>
            <input id="username" type="text" />

            {/* tabIndex */}
            <button tabIndex={1}>第一个</button>
            <button tabIndex={2}>第二个</button>

            {/* data-* 属性 */}
            <div data-testid="test-element" data-user-id="123">
                自定义数据属性
            </div>

            {/* aria-* 无障碍属性 */}
            <button aria-label="关闭" aria-pressed="false">
                ×
            </button>
        </div>
    );
}


// ============================================================
//                    5. JSX 中的注释
// ============================================================

function CommentsExample() {
    return (
        <div>
            {/* 这是 JSX 中的注释 */}

            {/*
                多行注释
                也是这样写
            */}

            <p>内容</p>

            {/* 注释掉元素
            <p>这个元素被注释了</p>
            */}
        </div>
    );
}


// ============================================================
//                    6. JSX 防止注入攻击
// ============================================================

/**
 * 【XSS 防护】
 *
 * React DOM 在渲染前会对所有值进行转义
 * 这可以防止 XSS(跨站脚本)攻击
 *
 * 如果确实需要渲染 HTML,使用 dangerouslySetInnerHTML
 * 但要确保内容是安全的
 */

function XSSPrevention() {
    // 恶意输入会被转义,不会执行
    const userInput = '<script>alert("xss")</script>';

    // 安全的 HTML 内容(来自可信源)
    const safeHTML = '<strong>加粗文字</strong>';

    return (
        <div>
            {/* 自动转义,安全 */}
            <p>{userInput}</p>

            {/*
                危险!只在确保内容安全时使用
                比如:内容来自 CMS 的富文本编辑器
            */}
            <div dangerouslySetInnerHTML={{ __html: safeHTML }} />
        </div>
    );
}


// ============================================================
//                    7. JSX 最佳实践
// ============================================================

/**
 * 【最佳实践】
 *
 * 1. 保持 JSX 简洁
 *    - 复杂逻辑提取到函数或变量
 *    - 避免过深的嵌套
 *
 * 2. 组件拆分
 *    - 一个组件只做一件事
 *    - 可复用的部分提取为独立组件
 *
 * 3. 条件渲染
 *    - 简单条件用 && 或三元表达式
 *    - 复杂条件用函数或提前返回
 *
 * 4. key 的使用
 *    - 使用稳定唯一的标识符
 *    - 避免使用数组索引(除非静态列表)
 */

// --- 好的实践 ---
function GoodPractice() {
    const items = [
        { id: 1, name: 'Item 1', completed: false },
        { id: 2, name: 'Item 2', completed: true },
    ];

    // 复杂逻辑提取到函数
    const renderItem = (item) => (
        <li key={item.id} className={item.completed ? 'completed' : ''}>
            {item.name}
        </li>
    );

    // 条件提前计算
    const completedCount = items.filter(i => i.completed).length;
    const hasCompleted = completedCount > 0;

    return (
        <div>
            <h2>待办列表</h2>
            {hasCompleted && <p>已完成: {completedCount}</p>}
            <ul>{items.map(renderItem)}</ul>
        </div>
    );
}

// --- 避免的写法 ---
function AvoidThis() {
    const data = [1, 2, 3];

    return (
        <div>
            {/* ❌ 避免: 过于复杂的内联逻辑 */}
            {data.length > 0 ? (
                data.filter(x => x > 1).map(x => (
                    <span key={x}>{x * 2}</span>
                ))
            ) : (
                <p>无数据</p>
            )}

            {/* ✅ 推荐: 提取逻辑 */}
            {/* 见上面 GoodPractice 的写法 */}
        </div>
    );
}


// ============================================================
//                    导出示例组件
// ============================================================

export {
    BasicElement,
    NestedElements,
    FragmentExample,
    VariablesAndExpressions,
    ConditionalRendering,
    ComplexConditional,
    BasicList,
    ObjectList,
    NestedList,
    FilteredList,
    StylingExample,
    PropsSpread,
    SpecialAttributes,
    CommentsExample,
    XSSPrevention,
    GoodPractice,
};

export default function JSXTutorial() {
    return (
        <div className="tutorial">
            <h1>JSX 基础教程</h1>
            <BasicElement />
            <VariablesAndExpressions />
            <ConditionalRendering />
            <ObjectList />
            <StylingExample />
        </div>
    );
}

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布