React JSX完全指南
1. JSX基础概念
什么是JSX?
JSX (JavaScript XML) 是React中用于描述用户界面的语法扩展。它允许你在JavaScript代码中写类似HTML的标记语言,让组件的结构更加直观。
JSX的特点
- 声明式语法:描述UI应该是什么样子
- JavaScript表达式:可以在JSX中嵌入任何JavaScript表达式
- 类型安全:配合TypeScript可以进行类型检查
- 编译时转换:JSX会被编译成普通的JavaScript函数调用
JSX vs HTML
// JSX
const element = <h1 className="greeting">Hello, World!</h1>;// 编译后的JavaScript
const element = React.createElement('h1',{ className: 'greeting' },'Hello, World!'
);// 对比HTML
<h1 class="greeting">Hello, World!</h1>
2. JSX基础语法
2.1 基本结构
import React from 'react';function Welcome() {return (<div><h1>欢迎使用React</h1><p>这是一个JSX示例</p></div>);
}// 或者使用Fragment避免额外的div
function WelcomeFragment() {return (<React.Fragment><h1>欢迎使用React</h1><p>这是一个JSX示例</p></React.Fragment>);
}// Fragment简写
function WelcomeShort() {return (<><h1>欢迎使用React</h1><p>这是一个JSX示例</p></>);
}
2.2 自闭合标签
function SelfClosingTags() {return (<div>{/* 自闭合标签必须以 /> 结尾 */}<img src="image.jpg" alt="示例图片" /><input type="text" placeholder="输入内容" /><br /><hr /></div>);
}
2.3 JavaScript表达式嵌入
function ExpressionExample() {const name = 'React';const user = {firstName: '张',lastName: '三'};const items = ['苹果', '香蕉', '橙子'];return (<div>{/* 基本变量 */}<h1>Hello, {name}!</h1>{/* 对象属性 */}<p>用户: {user.firstName}{user.lastName}</p>{/* 函数调用 */}<p>当前时间: {new Date().toLocaleString()}</p>{/* 三元运算符 */}<p>状态: {user ? '已登录' : '未登录'}</p>{/* 数组长度 */}<p>商品数量: {items.length}</p>{/* 数学运算 */}<p>计算结果: {2 + 3 * 4}</p>{/* 字符串方法 */}<p>大写名称: {name.toUpperCase()}</p></div>);
}
2.4 属性设置
function AttributeExample() {const imageUrl = 'https://example.com/image.jpg';const altText = '示例图片';const isDisabled = true;const className = 'primary-button';return (<div>{/* 字符串属性 */}<img src="static-image.jpg" alt="静态图片" />{/* 动态属性 */}<img src={imageUrl} alt={altText} />{/* 布尔属性 */}<button disabled={isDisabled}>按钮</button><input type="checkbox" checked={true} readOnly />{/* className代替class */}<div className={className}>样式化的div</div>{/* 多个类名 */}<div className={`base-class ${className} ${isDisabled ? 'disabled' : ''}`}>多类名div</div>{/* htmlFor代替for */}<label htmlFor="username">用户名:</label><input id="username" type="text" /></div>);
}
3. 条件渲染
3.1 if语句(组件外部)
function ConditionalRendering({ user, isLoading }) {// 早期返回if (isLoading) {return <div className="loading">加载中...</div>;}if (!user) {return <div className="error">用户未找到</div>;}return (<div className="user-profile"><h1>欢迎, {user.name}!</h1><p>邮箱: {user.email}</p></div>);
}
3.2 三元运算符
function TernaryExample({ isLoggedIn, user, hasNotifications }) {return (<div><header>{isLoggedIn ? (<div className="user-menu"><span>欢迎, {user.name}</span><button>退出</button></div>) : (<div className="auth-buttons"><button>登录</button><button>注册</button></div>)}</header>{/* 嵌套三元运算符(不推荐过度使用) */}<div className="notifications">{isLoggedIn ? (hasNotifications ? (<span className="badge">有新消息</span>) : (<span>无新消息</span>)) : (<span>请先登录</span>)}</div></div>);
}
3.3 逻辑与运算符
function LogicalAndExample({ user, showWelcome, isVip, errors }) {return (<div>{/* 简单条件渲染 */}{showWelcome && <h1>欢迎回来!</h1>}{/* 基于用户状态 */}{user && <p>你好, {user.name}</p>}{/* VIP标识 */}{user && isVip && <span className="vip-badge">VIP用户</span>}{/* 错误信息显示 */}{errors && errors.length > 0 && (<div className="error-list">{errors.map((error, index) => (<p key={index} className="error">{error}</p>))}</div>)}{/* 注意:避免使用数字0,因为0会被渲染 */}{/* ❌ 错误示例 */}{user.posts.length && <PostList posts={user.posts} />}{/* ✅ 正确示例 */}{user.posts.length > 0 && <PostList posts={user.posts} />}{Boolean(user.posts.length) && <PostList posts={user.posts} />}</div>);
}
3.4 条件渲染的高级模式
function AdvancedConditional({ status, user, permissions }) {// 使用函数进行复杂条件判断const renderContent = () => {switch (status) {case 'loading':return <LoadingSpinner />;case 'error':return <ErrorMessage message="加载失败" />;case 'empty':return <EmptyState message="暂无数据" />;case 'success':return <UserContent user={user} />;default:return <div>未知状态</div>;}};// 使用对象映射const statusComponents = {loading: <LoadingSpinner />,error: <ErrorMessage message="加载失败" />,empty: <EmptyState message="暂无数据" />,success: <UserContent user={user} />};return (<div><h1>用户面板</h1>{/* 方法1: 函数调用 */}{renderContent()}{/* 方法2: 对象映射 */}{statusComponents[status] || <div>未知状态</div>}{/* 权限控制 */}{permissions.canEdit && <EditButton />}{permissions.canDelete && <DeleteButton />}{permissions.isAdmin && <AdminPanel />}</div>);
}
4. 列表渲染
4.1 基本列表渲染
function BasicList() {const fruits = ['苹果', '香蕉', '橙子', '葡萄'];const numbers = [1, 2, 3, 4, 5];return (<div>{/* 简单数组渲染 */}<ul>{fruits.map((fruit, index) => (<li key={index}>{fruit}</li>))}</ul>{/* 数字列表 */}<ol>{numbers.map(number => (<li key={number}>{number * 2}</li>))}</ol></div>);
}
4.2 对象数组渲染
function ObjectList() {const users = [{ id: 1, name: '张三', email: 'zhangsan@example.com', isActive: true },{ id: 2, name: '李四', email: 'lisi@example.com', isActive: false },{ id: 3, name: '王五', email: 'wangwu@example.com', isActive: true }];const products = [{ id: 'p1', name: 'iPhone', price: 6999, category: '手机' },{ id: 'p2', name: 'MacBook', price: 12999, category: '电脑' },{ id: 'p3', name: 'iPad', price: 3999, category: '平板' }];return (<div><h2>用户列表</h2><div className="user-list">{users.map(user => (<div key={user.id} className={`user-card ${user.isActive ? 'active' : 'inactive'}`}><h3>{user.name}</h3><p>邮箱: {user.email}</p><span className="status">{user.isActive ? '在线' : '离线'}</span></div>))}</div><h2>产品列表</h2><div className="product-grid">{products.map(product => (<ProductCardkey={product.id}id={product.id}name={product.name}price={product.price}category={product.category}/>))}</div></div>);
}function ProductCard({ id, name, price, category }) {return (<div className="product-card"><h4>{name}</h4><p className="category">{category}</p><p className="price">¥{price.toLocaleString()}</p></div>);
}
4.3 条件过滤和排序
function FilteredList() {const [filter, setFilter] = useState('all');const [sortBy, setSortBy] = useState('name');const todos = [{ id: 1, text: '学习React', completed: true, priority: 'high' },{ id: 2, text: '写文档', completed: false, priority: 'medium' },{ id: 3, text: '测试代码', completed: false, priority: 'high' },{ id: 4, text: '部署应用', completed: true, priority: 'low' }];// 过滤逻辑const filteredTodos = todos.filter(todo => {switch (filter) {case 'completed':return todo.completed;case 'active':return !todo.completed;case 'high':return todo.priority === 'high';default:return true;}});// 排序逻辑const sortedTodos = [...filteredTodos].sort((a, b) => {switch (sortBy) {case 'priority':const priorityOrder = { high: 3, medium: 2, low: 1 };return priorityOrder[b.priority] - priorityOrder[a.priority];case 'status':return a.completed - b.completed;default:return a.text.localeCompare(b.text);}});return (<div><div className="filters"><select value={filter} onChange={e => setFilter(e.target.value)}><option value="all">全部</option><option value="completed">已完成</option><option value="active">待完成</option><option value="high">高优先级</option></select><select value={sortBy} onChange={e => setSortBy(e.target.value)}><option value="name">按名称排序</option><option value="priority">按优先级排序</option><option value="status">按状态排序</option></select></div><div className="todo-list">{sortedTodos.length > 0 ? (sortedTodos.map(todo => (<div key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}><inputtype="checkbox"checked={todo.completed}onChange={() => {/* toggle logic */}}/><span className="text">{todo.text}</span><span className={`priority ${todo.priority}`}>{todo.priority}</span></div>))) : (<p>没有找到匹配的项目</p>)}</div></div>);
}
4.4 Key的正确使用
function KeyExamples() {const [items, setItems] = useState([{ id: 'a', name: '项目A', order: 1 },{ id: 'b', name: '项目B', order: 2 },{ id: 'c', name: '项目C', order: 3 }]);const shuffleItems = () => {setItems([...items].sort(() => Math.random() - 0.5));};return (<div><button onClick={shuffleItems}>随机排序</button>{/* ✅ 正确:使用稳定的唯一ID作为key */}<ul className="correct-keys">{items.map(item => (<ItemComponent key={item.id} item={item} />))}</ul>{/* ❌ 错误:使用索引作为key(在动态列表中) */}<ul className="incorrect-keys">{items.map((item, index) => (<ItemComponent key={index} item={item} />))}</ul>{/* ✅ 正确:静态列表可以使用索引 */}<ul className="static-list">{['首页', '关于', '联系'].map((page, index) => (<li key={index}>{page}</li>))}</ul></div>);
}function ItemComponent({ item }) {const [count, setCount] = useState(0);return (<li>{item.name} (点击次数: {count})<button onClick={() => setCount(c => c + 1)}>点击</button></li>);
}
5. 事件处理
5.1 基本事件处理
function EventHandling() {const [message, setMessage] = useState('');const [inputValue, setInputValue] = useState('');// 简单事件处理const handleClick = () => {setMessage('按钮被点击了!');};// 带参数的事件处理const handleClickWithParam = (text) => {setMessage(`点击了: ${text}`);};// 输入事件处理const handleInputChange = (e) => {setInputValue(e.target.value);};// 表单提交const handleSubmit = (e) => {e.preventDefault();setMessage(`提交的内容: ${inputValue}`);};return (<div><p>{message}</p>{/* 基本点击事件 */}<button onClick={handleClick}>点击我</button>{/* 内联函数(不推荐在性能敏感场景使用) */}<button onClick={() => setMessage('内联函数点击')}>内联事件</button>{/* 传递参数 */}<button onClick={() => handleClickWithParam('参数按钮')}>带参数点击</button>{/* 表单处理 */}<form onSubmit={handleSubmit}><inputtype="text"value={inputValue}onChange={handleInputChange}placeholder="输入内容"/><button type="submit">提交</button></form></div>);
}
5.2 常用事件类型
function CommonEvents() {const [logs, setLogs] = useState([]);const addLog = (message) => {setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`]);};return (<div><div className="logs"><h3>事件日志:</h3>{logs.map((log, index) => (<p key={index}>{log}</p>))}</div>{/* 鼠标事件 */}<buttononClick={() => addLog('点击事件')}onDoubleClick={() => addLog('双击事件')}onMouseEnter={() => addLog('鼠标进入')}onMouseLeave={() => addLog('鼠标离开')}onMouseDown={() => addLog('鼠标按下')}onMouseUp={() => addLog('鼠标抬起')}>鼠标事件按钮</button>{/* 键盘事件 */}<inputtype="text"placeholder="键盘事件测试"onKeyDown={(e) => addLog(`按键按下: ${e.key}`)}onKeyUp={(e) => addLog(`按键抬起: ${e.key}`)}onKeyPress={(e) => addLog(`按键按压: ${e.key}`)}/>{/* 表单事件 */}<inputtype="text"placeholder="表单事件测试"onChange={(e) => addLog(`输入改变: ${e.target.value}`)}onFocus={() => addLog('获得焦点')}onBlur={() => addLog('失去焦点')}/>{/* 其他事件 */}<divstyle={{ width: 200, height: 100, border: '1px solid #ccc' }}onScroll={() => addLog('滚动事件')}onResize={() => addLog('大小改变')}>可滚动区域</div></div>);
}
5.3 事件对象和阻止默认行为
function EventObject() {const [position, setPosition] = useState({ x: 0, y: 0 });const handleMouseMove = (e) => {setPosition({ x: e.clientX, y: e.clientY });};const handleContextMenu = (e) => {e.preventDefault(); // 阻止右键菜单console.log('右键点击被阻止');};const handleLinkClick = (e) => {e.preventDefault();console.log('链接点击被拦截');};const handleFormSubmit = (e) => {e.preventDefault();const formData = new FormData(e.target);console.log('表单数据:', Object.fromEntries(formData));};const handleKeyDown = (e) => {// 阻止特定按键if (e.key === 'Enter' && e.ctrlKey) {e.preventDefault();console.log('Ctrl+Enter被拦截');}};return (<div><p>鼠标位置: X={position.x}, Y={position.y}</p>{/* 鼠标追踪 */}<divstyle={{ width: 300, height: 200, border: '1px solid #ccc',backgroundColor: '#f0f0f0'}}onMouseMove={handleMouseMove}onContextMenu={handleContextMenu}>移动鼠标查看坐标(右键被禁用)</div>{/* 阻止链接跳转 */}<a href="https://example.com" onClick={handleLinkClick}>被拦截的链接</a>{/* 表单提交拦截 */}<form onSubmit={handleFormSubmit}><input name="username" placeholder="用户名" /><input name="email" placeholder="邮箱" /><button type="submit">提交(被拦截)</button></form>{/* 键盘事件拦截 */}<textareaplaceholder="尝试按 Ctrl+Enter"onKeyDown={handleKeyDown}/></div>);
}
6. 样式和CSS
6.1 内联样式
function InlineStyles() {const [isActive, setIsActive] = useState(false);const [theme, setTheme] = useState('light');// 样式对象const containerStyle = {padding: '20px',margin: '10px',border: '1px solid #ddd',borderRadius: '8px'};// 动态样式const buttonStyle = {backgroundColor: isActive ? '#007bff' : '#6c757d',color: 'white',border: 'none',padding: '10px 15px',borderRadius: '4px',cursor: 'pointer',transition: 'all 0.3s ease'};// 主题样式const themeStyles = {light: {backgroundColor: '#ffffff',color: '#333333'},dark: {backgroundColor: '#333333',color: '#ffffff'}};return (<div style={{...containerStyle, ...themeStyles[theme]}}><h2>内联样式示例</h2>{/* 基本内联样式 */}<p style={{ fontSize: '16px', fontWeight: 'bold', color: 'blue' }}>蓝色粗体文本</p>{/* 动态样式 */}<buttonstyle={buttonStyle}onClick={() => setIsActive(!isActive)}>{isActive ? '激活' : '非激活'}</button>{/* 条件样式 */}<div style={{width: '100px',height: '100px',backgroundColor: isActive ? 'green' : 'red',margin: '10px 0',transition: 'background-color 0.3s ease'}}>状态方块</div>{/* 主题切换 */}<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>切换主题 ({theme})</button></div>);
}
6.2 CSS类名
import './styles.css'; // 假设存在样式文件function CSSClasses() {const [isVisible, setIsVisible] = useState(true);const [status, setStatus] = useState('normal');const [size, setSize] = useState('medium');// 条件类名const getButtonClasses = () => {let classes = ['btn'];if (status === 'success') classes.push('btn-success');else if (status === 'error') classes.push('btn-error');else classes.push('btn-normal');classes.push(`btn-${size}`);return classes.join(' ');};return (<div className="container"><h2 className="title">CSS类名示例</h2>{/* 基本类名 */}<div className="card"><p className="text">基本卡片内容</p></div>{/* 条件类名 */}<div className={`alert ${isVisible ? 'alert-visible' : 'alert-hidden'}`}>{isVisible ? '显示的警告' : '隐藏的警告'}</div>{/* 多个条件类名 */}<buttonclassName={`btn ${status === 'success' ? 'btn-success' : ''} ${status === 'error' ? 'btn-error' : ''} ${size === 'large' ? 'btn-large' : ''} ${isVisible ? 'visible' : 'hidden'}`.trim()}onClick={() => setIsVisible(!isVisible)}>多条件按钮</button>{/* 使用函数生成类名 */}<button className={getButtonClasses()}>函数生成类名</button>{/* 模板字符串类名 */}<div className={`item item-${status} size-${size}`}>模板字符串类名</div><div className="controls"><select value={status} onChange={e => setStatus(e.target.value)}><option value="normal">普通</option><option value="success">成功</option><option value="error">错误</option></select><select value={size} onChange={e => setSize(e.target.value)}><option value="small">小</option><option value="medium">中</option><option value="large">大</option></select></div></div>);
}
6.3 CSS模块化
// styles.module.css文件内容示例:
/*
.container {padding: 20px;max-width: 800px;margin: 0 auto;
}.title {color: #333;font-size: 2em;text-align: center;
}.card {background: white;border-radius: 8px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);padding: 16px;margin: 16px 0;
}
*/import styles from './styles.module.css';function CSSModules() {const [isHighlighted, setIsHighlighted] = useState(false);return (<div className={styles.container}><h1 className={styles.title}>CSS模块示例</h1><div className={styles.card}><p>这是一个使用CSS模块的卡片</p></div>{/* 组合多个模块类 */}<div className={`${styles.card} ${isHighlighted ? styles.highlighted : ''}`}><p>可切换高亮的卡片</p><button onClick={() => setIsHighlighted(!isHighlighted)}>切换高亮</button></div></div>);
}
6.4 CSS-in-JS (styled-components示例)
import styled from 'styled-components';// 基础样式组件
const Container = styled.div`padding: 20px;background-color: #f5f5f5;border-radius: 8px;
`;// 带props的样式组件
const Button = styled.button`background-color: ${props => props.primary ? '#007bff' : '#6c757d'};color: white;border: none;padding: 10px 15px;border-radius: 4px;cursor: pointer;font-size: ${props => props.size === 'large' ? '18px' : '14px'};&:hover {opacity: 0.8;}&:disabled {background-color: #ccc;cursor: not-allowed;}
`;// 扩展样式组件
const PrimaryButton = styled(Button)`background-color: #28a745;&:hover {background-color: #218838;}
`;function StyledComponentsExample() {const [disabled, setDisabled] = useState(false);return (<Container><h2>CSS-in-JS 示例</h2><Button>普通按钮</Button><Button primary>主要按钮</Button><Button size="large">大按钮</Button><Button disabled={disabled}>{disabled ? '已禁用' : '可点击'}</Button><PrimaryButton onClick={() => setDisabled(!disabled)}>切换按钮状态</PrimaryButton></Container>);
}
7. 高级JSX特性
7.1 扩展运算符传递Props
function PropsSpread() {const buttonProps = {type: 'submit',disabled: false,className: 'primary-btn',onClick: () => console.log('点击')};const userInfo = {name: '张三',age: 25,email: 'zhangsan@example.com'};return (<div>{/* 扩展对象作为props */}<button {...buttonProps}>扩展属性按钮</button>{/* 扩展 + 覆盖属性 */}<button {...buttonProps} disabled={true}>覆盖disabled属性</button>{/* 用户信息组件 */}<UserCard {...userInfo} /><UserCard {...userInfo} age={30} /> {/* 覆盖age */}</div>);
}function UserCard({ name, age, email, ...otherProps }) {return (<div className="user-card" {...otherProps}><h3>{name}</h3><p>年龄: {age}</p><p>邮箱: {email}</p></div>);
}
7.2 Children和组合模式
function ChildrenExamples() {return (<div>{/* 基本children使用 */}<Card><h3>卡片标题</h3><p>这是卡片内容</p></Card>{/* 多个children */}<Modalheader={<h2>模态框标题</h2>}footer={<div><button>取消</button><button>确认</button></div>}><p>模态框内容</p><p>可以有多个子元素</p></Modal>{/* render props */}<DataProvider>{(data, loading) => (loading ? <div>加载中...</div> : <div>数据: {data}</div>)}</DataProvider></div>);
}// 基础Card组件
function Card({ children, className = '' }) {return (<div className={`card ${className}`}>{children}</div>);
}// 带多个插槽的Modal组件
function Modal({ header, footer, children, isOpen = true }) {if (!isOpen) return null;return (<div className="modal-overlay"><div className="modal"><header className="modal-header">{header}</header><main className="modal-body">{children}</main><footer className="modal-footer">{footer}</footer></div></div>);
}// 使用render props的组件
function DataProvider({ children }) {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => {setTimeout(() => {setData('模拟数据');setLoading(false);}, 1000);}, []);return children(data, loading);
}
7.3 动态组件渲染
function DynamicComponents() {const [componentType, setComponentType] = useState('button');// 组件映射const components = {button: Button,input: Input,card: Card,list: List};const ComponentToRender = components[componentType];const componentProps = {button: { children: '动态按钮', onClick: () => alert('点击') },input: { placeholder: '动态输入框', type: 'text' },card: { children: <p>动态卡片内容</p> },list: { items: ['项目1', '项目2', '项目3'] }};return (<div><h3>动态组件渲染</h3><select value={componentType} onChange={e => setComponentType(e.target.value)}><option value="button">按钮</option><option value="input">输入框</option><option value="card">卡片</option><option value="list">列表</option></select><div style={{ margin: '20px 0' }}><ComponentToRender {...componentProps[componentType]} /></div></div>);
}function Button({ children, onClick }) {return <button onClick={onClick}>{children}</button>;
}function Input({ placeholder, type }) {return <input type={type} placeholder={placeholder} />;
}function List({ items }) {return (<ul>{items.map((item, index) => (<li key={index}>{item}</li>))}</ul>);
}
8. JSX转换原理
8.1 JSX到JavaScript的转换
// JSX代码
const element = (<div className="container"><h1>Hello, {name}!</h1><button onClick={handleClick}>点击我</button></div>
);// 转换后的JavaScript (React 17之前)
const element = React.createElement('div',{ className: 'container' },React.createElement('h1', null, 'Hello, ', name, '!'),React.createElement('button', { onClick: handleClick }, '点击我')
);// 转换后的JavaScript (React 17+新的JSX Transform)
import { jsx as _jsx, jsxs as _jsxs } from 'react/jsx-runtime';const element = _jsxs('div', {className: 'container',children: [_jsxs('h1', {children: ['Hello, ', name, '!']}),_jsx('button', {onClick: handleClick,children: '点击我'})]
});
8.2 自定义JSX元素
// 自定义组件的JSX转换
function MyComponent({ title, children }) {return <div className="my-component">{title}{children}</div>;
}const app = <MyComponent title="标题">内容</MyComponent>;// 转换后
const app = React.createElement(MyComponent,{ title: '标题' },'内容'
);
9. 性能优化
9.1 避免内联对象和函数
// ❌ 不好的做法
function BadPerformance() {return (<div>{items.map(item => (<ItemComponentkey={item.id}item={item}style={{ margin: '10px' }} // 每次渲染都创建新对象onClick={() => handleClick(item.id)} // 每次渲染都创建新函数/>))}</div>);
}// ✅ 好的做法
function GoodPerformance() {// 将样式对象提取到组件外部或使用useMemoconst itemStyle = useMemo(() => ({ margin: '10px' }), []);// 使用useCallback缓存函数const handleItemClick = useCallback((itemId) => {handleClick(itemId);}, []);return (<div>{items.map(item => (<ItemComponentkey={item.id}item={item}style={itemStyle}onClick={() => handleItemClick(item.id)}/>))}</div>);
}// 或者更好的做法:将点击逻辑传给子组件
function BestPerformance() {const handleItemClick = useCallback((itemId) => {handleClick(itemId);}, []);return (<div>{items.map(item => (<ItemComponentkey={item.id}item={item}onItemClick={handleItemClick}/>))}</div>);
}
9.2 合理使用Fragment
function FragmentOptimization() {const [showDetails, setShowDetails] = useState(false);return (<div><h1>主标题</h1>{/* ❌ 不必要的wrapper div */}<div><p>段落1</p><p>段落2</p></div>{/* ✅ 使用Fragment避免额外DOM节点 */}<React.Fragment><p>段落3</p><p>段落4</p></React.Fragment>{/* ✅ Fragment简写 */}<><p>段落5</p><p>段落6</p></>{/* 条件渲染中使用Fragment */}{showDetails && (<><h2>详细信息</h2><p>这里是详细内容</p><button onClick={() => setShowDetails(false)}>隐藏详情</button></>)}</div>);
}
10. 常见错误和解决方案
10.1 常见JSX错误
// ❌ 常见错误示例
function CommonMistakes() {const items = [];const user = null;return (<div>{/* 错误1: 忘记key */}{items.map(item => <div>{item.name}</div>)}{/* 错误2: class替代className */}<div class="container">错误的class</div>{/* 错误3: for替代htmlFor */}<label for="input">错误的for</label><input id="input" />{/* 错误4: 自闭合标签没有闭合 */}<input type="text"><img src="image.jpg">{/* 错误5: 返回多个根元素 */}<h1>标题1</h1><h2>标题2</h2>{/* 错误6: 条件渲染可能显示0 */}{items.length && <ItemList items={items} />}{/* 错误7: 直接渲染对象 */}<p>用户: {user}</p></div>);
}// ✅ 正确的写法
function CorrectedMistakes() {const items = [];const user = { name: '张三', id: 1 };return (<>{/* 正确1: 添加key */}{items.map(item => <div key={item.id}>{item.name}</div>)}{/* 正确2: 使用className */}<div className="container">正确的className</div>{/* 正确3: 使用htmlFor */}<label htmlFor="input">正确的htmlFor</label><input id="input" />{/* 正确4: 自闭合标签正确闭合 */}<input type="text" /><img src="image.jpg" alt="图片" />{/* 正确5: 使用Fragment或单一根元素 */}{/* 正确6: 避免0的显示 */}{items.length > 0 && <ItemList items={items} />}{/* 正确7: 渲染对象属性 */}<p>用户: {user?.name}</p></>);
}
10.2 调试JSX
function JSXDebugging() {const [data, setData] = useState(null);const [error, setError] = useState(null);// 调试技巧1: 使用console.logconsole.log('组件渲染', { data, error });// 调试技巧2: 条件渲染调试信息const isDebugging = process.env.NODE_ENV === 'development';return (<div>{/* 调试信息 */}{isDebugging && (<div style={{ background: '#f0f0f0', padding: '10px', margin: '10px' }}><h4>调试信息:</h4><pre>{JSON.stringify({ data, error }, null, 2)}</pre></div>)}{/* 错误边界显示 */}{error ? (<div className="error">错误: {error.message}<button onClick={() => setError(null)}>清除错误</button></div>) : (<div>{data ? (<div>数据: {JSON.stringify(data)}</div>) : (<div>无数据</div>)}</div>)}{/* React DevTools中的标识 */}<div data-testid="main-content" data-component="JSXDebugging">主要内容区域</div></div>);
}
11. 最佳实践总结
11.1 代码组织
// ✅ 好的组件结构
function WellStructuredComponent({ title, items, onItemClick, className = '',...otherProps
}) {// 1. Hooks在顶部const [selectedId, setSelectedId] = useState(null);const [filter, setFilter] = useState('');// 2. 计算属性和派生状态const filteredItems = useMemo(() => {return items.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()));}, [items, filter]);// 3. 事件处理函数const handleItemClick = useCallback((item) => {setSelectedId(item.id);onItemClick?.(item);}, [onItemClick]);// 4. 渲染函数const renderItem = (item) => (<divkey={item.id}className={`item ${selectedId === item.id ? 'selected' : ''}`}onClick={() => handleItemClick(item)}>{item.name}</div>);// 5. 早期返回if (!items?.length) {return <div className="empty">无数据</div>;}// 6. 主要渲染return (<div className={`component ${className}`} {...otherProps}><h2>{title}</h2><inputtype="text"value={filter}onChange={e => setFilter(e.target.value)}placeholder="搜索..."/><div className="items">{filteredItems.map(renderItem)}</div></div>);
}
11.2 性能考虑
// 性能优化的JSX写法
function PerformantComponent() {// 静态内容提取到组件外const staticContent = (<div className="static"><p>这是静态内容,不会重新渲染</p></div>);return (<div>{staticContent}{/* 动态内容 */}<DynamicContent /></div>);
}// 使用memo优化子组件
const OptimizedChild = memo(function OptimizedChild({ data, onChange }) {return (<div><p>{data.name}</p><button onClick={() => onChange(data.id)}>点击</button></div>);
});
JSX是React的核心特性,掌握这些语法和最佳实践将帮助你编写更高效、更易维护的React应用。记住要保持JSX代码简洁明了,合理使用条件渲染和列表渲染,并注意性能优化。