React JSX 语法详解
第二章 JSX 语法详解
JSX(JavaScript XML)是 React 的核心语法扩展,它允许我们在 JavaScript 中编写类似 HTML 的代码,使得组件的结构更加直观和易于理解。
2.1 JSX 基础概念
2.1.1 JSX 的本质
JSX 编译过程:
graph TD
A[JSX 代码] --> B[Babel 编译器]
B --> C[React.createElement 调用]
C --> D[Virtual DOM 对象]
D --> E[真实 DOM]
F[TypeScript] --> G[类型检查]
G --> A
JSX 转换示例:
// JSX 代码
const element = (
<div className="container">
<h1>Hello World</h1>
<p>Current time: {new Date().toLocaleTimeString()}</p>
</div>
);
// 编译后的 JavaScript 代码(React 17+)
const element = React.createElement(
'div',
{ className: 'container' },
React.createElement('h1', null, 'Hello World'),
React.createElement(
'p',
null,
'Current time: ',
new Date().toLocaleTimeString()
)
);
// React 17+ 自动转换(不需要手动导入 React)
import { jsx as _jsx } from 'react/jsx-runtime';
const element = _jsx('div', {
className: 'container',
children: [
_jsx('h1', { children: 'Hello World' }),
_jsx('p', {
children: ['Current time: ', new Date().toLocaleTimeString()]
})
]
});
2.1.2 JSX 与 HTML 的区别
语法差异对比表:
| 特性 | HTML | JSX | 说明 |
|---|---|---|---|
| 属性命名 | class |
className |
避免与 JavaScript 关键字冲突 |
| 标签闭合 | <img>, <br> |
<img />, <br /> |
所有标签必须闭合 |
| 布尔属性 | disabled |
disabled={true} |
布尔值需显式指定 |
| 样式属性 | style="color: red" |
style={{ color: 'red' }} |
使用对象语法 |
| 注释 | <!-- comment --> |
{/* comment */} |
JavaScript 注释语法 |
| 自定义属性 | data-* |
data-* |
data 属性保持一致 |
| 事件命名 | onclick |
onClick |
驼峰命名法 |
具体示例对比:
<!-- HTML 代码 -->
<div class="container" style="margin: 0 auto; width: 100%">
<img src="logo.png" alt="Logo" class="logo" />
<button onclick="handleClick()" disabled>Submit</button>
<input type="text" placeholder="Enter name" required />
<!-- This is a comment -->
</div>
// JSX 代码
<div className="container" style={{ margin: '0 auto', width: '100%' }}>
<img src="logo.png" alt="Logo" className="logo" />
<button onClick={handleClick} disabled={true}>Submit</button>
<input type="text" placeholder="Enter name" required={true} />
{/* This is a comment */}
</div>
2.1.3 JSX 的类型定义
TypeScript JSX 类型:
// JSX.Element 类型
const element: JSX.Element = <div>Hello</div>;
// JSX.IntrinsicElements - 内置元素类型
declare global {
namespace JSX {
interface IntrinsicElements {
div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
// ... 其他内置元素
}
}
}
// 自定义组件类型
interface CustomComponentProps {
title: string;
count: number;
onAction?: () => void;
}
const CustomComponent: React.FC<CustomComponentProps> = ({ title, count, onAction }) => {
return (
<div>
<h2>{title}</h2>
<p>Count: {count}</p>
{onAction && <button onClick={onAction}>Action</button>}
</div>
);
};
// 使用自定义组件
const App = () => {
return (
<CustomComponent
title="My App"
count={42}
onAction={() => console.log('Action triggered')}
/>
);
};
2.2 JSX 核心语法
2.2.1 元素与组件
函数组件定义:
// 基础函数组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 箭头函数组件
const Welcome = (props) => {
return <h1>Hello, {props.name}</h1>;
};
// 箭头函数组件(隐式返回)
const Welcome = (props) => <h1>Hello, {props.name}</h1>;
// TypeScript 组件
interface WelcomeProps {
name: string;
age?: number;
}
const Welcome: React.FC<WelcomeProps> = ({ name, age }) => (
<h1>Hello, {name}!{age && ` Age: ${age}`}</h1>
);
// 使用组件
function App() {
return (
<div>
<Welcome name="Alice" />
<Welcome name="Bob" age={25} />
</div>
);
}
类组件定义:
import React, { Component } from 'react';
class Welcome extends Component {
constructor(props) {
super(props);
this.state = {
message: 'Welcome to React!'
};
}
render() {
const { name } = this.props;
const { message } = this.state;
return (
<div>
<h1>Hello, {name}</h1>
<p>{message}</p>
</div>
);
}
}
// TypeScript 类组件
interface WelcomeProps {
name: string;
}
interface WelcomeState {
message: string;
}
class Welcome extends Component<WelcomeProps, WelcomeState> {
constructor(props: WelcomeProps) {
super(props);
this.state = {
message: 'Welcome to React!'
};
}
render(): React.ReactNode {
const { name } = this.props;
const { message } = this.state;
return (
<div>
<h1>Hello, {name}</h1>
<p>{message}</p>
</div>
);
}
}
2.2.2 Props 与属性
Props 传递方式:
// 基础 Props 传递
const UserProfile = (props) => {
return (
<div>
<h2>{props.name}</h2>
<p>Age: {props.age}</p>
<p>Email: {props.email}</p>
</div>
);
};
// 解构 Props
const UserProfile = ({ name, age, email }) => {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
);
};
// 使用组件
function App() {
const user = {
name: 'John Doe',
age: 30,
email: 'john@example.com'
};
return (
<div>
{/* 分别传递属性 */}
<UserProfile
name="Alice"
age={25}
email="alice@example.com"
/>
{/* 使用展开运算符 */}
<UserProfile {...user} />
{/* 混合使用 */}
<UserProfile
{...user}
name="Bob" // 覆盖 name 属性
/>
</div>
);
}
Props 验证与默认值:
import PropTypes from 'prop-types';
const Button = ({
text,
onClick,
variant = 'primary',
disabled = false,
size = 'medium'
}) => {
const baseClasses = 'btn';
const variantClasses = `btn-${variant}`;
const sizeClasses = `btn-${size}`;
const disabledClasses = disabled ? 'btn-disabled' : '';
return (
<button
className={`${baseClasses} ${variantClasses} ${sizeClasses} ${disabledClasses}`}
onClick={onClick}
disabled={disabled}
>
{text}
</button>
);
};
// Props 类型验证
Button.propTypes = {
text: PropTypes.string.isRequired,
onClick: PropTypes.func,
variant: PropTypes.oneOf(['primary', 'secondary', 'danger', 'success']),
disabled: PropTypes.bool,
size: PropTypes.oneOf(['small', 'medium', 'large']),
};
// 默认 Props
Button.defaultProps = {
variant: 'primary',
disabled: false,
size: 'medium',
onClick: () => {},
};
// TypeScript 类型定义
interface ButtonProps {
text: string;
onClick?: () => void;
variant?: 'primary' | 'secondary' | 'danger' | 'success';
disabled?: boolean;
size?: 'small' | 'medium' | 'large';
}
const Button: React.FC<ButtonProps> = ({
text,
onClick,
variant = 'primary',
disabled = false,
size = 'medium'
}) => {
// 组件实现
};
2.2.3 Children 与内容
Children 传递方式:
// 基础 Children 使用
const Card = ({ children, title }) => {
return (
<div className="card">
{title && <div className="card-header">{title}</div>}
<div className="card-body">
{children}
</div>
</div>
);
};
// 使用 Card 组件
function App() {
return (
<div>
{/* 传递文本内容 */}
<Card title="Simple Card">
This is a simple card content.
</Card>
{/* 传递多个元素 */}
<Card title="Complex Card">
<h3>Subtitle</h3>
<p>Paragraph content.</p>
<button>Action Button</button>
</Card>
{/* 空内容 */}
<Card title="Empty Card">
{/* 可以显示空状态 */}
<p>No content available.</p>
</Card>
</div>
);
}
Children 操作函数:
import React, { Children, cloneElement, isValidElement } from 'react';
const TabContainer = ({ children, activeIndex, onTabChange }) => {
const tabs = Children.toArray(children);
return (
<div className="tab-container">
<div className="tab-nav">
{tabs.map((child, index) => {
if (!isValidElement(child)) return null;
const isActive = index === activeIndex;
const { title } = child.props;
return (
<button
key={index}
className={`tab-button ${isActive ? 'active' : ''}`}
onClick={() => onTabChange(index)}
>
{title}
</button>
);
})}
</div>
<div className="tab-content">
{tabs.map((child, index) => {
if (!isValidElement(child)) return null;
return (
<div
key={index}
className={`tab-panel ${index === activeIndex ? 'active' : ''}`}
>
{index === activeIndex ? child : null}
</div>
);
})}
</div>
</div>
);
};
const TabPanel = ({ title, children }) => {
return <div>{children}</div>;
};
// 使用示例
function App() {
const [activeTab, setActiveTab] = React.useState(0);
return (
<TabContainer
activeIndex={activeTab}
onTabChange={setActiveTab}
>
<TabPanel title="Profile">
<h3>User Profile</h3>
<p>Profile information goes here.</p>
</TabPanel>
<TabPanel title="Settings">
<h3>Settings</h3>
<p>Settings configuration goes here.</p>
</TabPanel>
<TabPanel title="About">
<h3>About</h3>
<p>About information goes here.</p>
</TabPanel>
</TabContainer>
);
}
Children 类型检查:
import { Children, isValidElement } from 'react';
const Menu = ({ children }) => {
// 检查 children 是否为 MenuItem
const menuItems = Children.toArray(children).filter(child =>
isValidElement(child) && child.type === MenuItem
);
if (menuItems.length === 0) {
console.warn('Menu should contain at least one MenuItem');
return null;
}
return (
<ul className="menu">
{menuItems.map((child, index) => (
<li key={index} className="menu-item">
{child}
</li>
))}
</ul>
);
};
const MenuItem = ({ children, onClick, disabled = false }) => {
return (
<button
className={`menu-button ${disabled ? 'disabled' : ''}`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
};
// 使用示例
function App() {
return (
<Menu>
<MenuItem onClick={() => console.log('Home clicked')}>
Home
</MenuItem>
<MenuItem onClick={() => console.log('About clicked')}>
About
</MenuItem>
<MenuItem onClick={() => console.log('Contact clicked')}>
Contact
</MenuItem>
</Menu>
);
}
2.3 JSX 表达式与插值
2.3.1 基础表达式插值
JavaScript 表达式嵌入:
import React, { useState } from 'react';
const ExpressionDemo = () => {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const [isLogin, setIsLogin] = useState(false);
const items = ['Apple', 'Banana', 'Orange'];
const user = {
id: 1,
name: 'John Doe',
email: 'john@example.com'
};
// 计算属性
const greeting = `Hello, ${user.name}!`;
const itemCount = items.length;
const isEven = count % 2 === 0;
return (
<div className="expression-demo">
{/* 变量插值 */}
<h1>{greeting}</h1>
{/* 算术运算 */}
<p>Count: {count}</p>
<p>Double: {count * 2}</p>
<p>Is even: {isEven ? 'Yes' : 'No'}</p>
{/* 字符串操作 */}
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter your name"
/>
{name && <p>Hello, {name.toUpperCase()}!</p>}
{/* 条件渲染 */}
<div>
{isLogin ? (
<p>Welcome back!</p>
) : (
<p>Please log in</p>
)}
</div>
{/* 数组操作 */}
<ul>
{items.map((item, index) => (
<li key={index}>
{index + 1}. {item}
</li>
))}
</ul>
{/* 对象操作 */}
<div className="user-card">
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>ID: {String(user.id).padStart(6, '0')}</p>
</div>
{/* 日期格式化 */}
<p>Current time: {new Date().toLocaleTimeString()}</p>
<p>Formatted date: {new Date().toLocaleDateString('zh-CN')}</p>
{/* 函数调用 */}
<button onClick={() => setCount(count + 1)}>
Increment: {count + 1}
</button>
</div>
);
};
2.3.2 条件渲染
多种条件渲染方式:
import React, { useState } from 'react';
const ConditionalRendering = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
const [userType, setUserType] = useState('guest');
// 模拟数据获取
const fetchData = async () => {
setIsLoading(true);
setError(null);
try {
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 1000));
setData({ message: 'Data loaded successfully!' });
} catch (err) {
setError('Failed to load data');
} finally {
setIsLoading(false);
}
};
// 1. 三元运算符条件渲染
const renderStatus = () => {
return isLoading ? (
<div className="loading">Loading...</div>
) : error ? (
<div className="error">Error: {error}</div>
) : data ? (
<div className="success">{data.message}</div>
) : (
<div className="idle">No data loaded</div>
);
};
return (
<div className="conditional-rendering">
<h2>Conditional Rendering Examples</h2>
{/* 三元运算符 */}
<div className="example">
<h3>1. Ternary Operator</h3>
<div className="status">
{isLoading ? 'Loading...' : 'Load Complete'}
</div>
</div>
{/* 逻辑与 (&&) */}
<div className="example">
<h3>2. Logical AND</h3>
{error && <div className="error">Error: {error}</div>}
{data && <div className="data">{data.message}</div>}
{!isLoading && !error && !data && (
<div className="no-data">No data available</div>
)}
</div>
{/* 函数条件渲染 */}
<div className="example">
<h3>3. Function Rendering</h3>
{renderStatus()}
</div>
{/* 多条件渲染 */}
<div className="example">
<h3>4. Multiple Conditions</h3>
<div className="user-info">
{(() => {
switch (userType) {
case 'admin':
return <span className="admin">Admin User</span>;
case 'user':
return <span className="user">Regular User</span>;
case 'guest':
return <span className="guest">Guest User</span>;
default:
return <span className="unknown">Unknown User</span>;
}
})()}
</div>
<div>
<label>User Type:</label>
<select
value={userType}
onChange={(e) => setUserType(e.target.value)}
>
<option value="guest">Guest</option>
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>
</div>
{/* 复杂条件渲染 */}
<div className="example">
<h3>5. Complex Conditions</h3>
<div className="actions">
{data && !isLoading && (
<div className="action-buttons">
{data.message.includes('success') && (
<button className="success-btn">Success Action</button>
)}
{data.message.includes('error') && (
<button className="error-btn">Error Action</button>
)}
{data.message.length > 20 && (
<button className="long-btn">Long Message Action</button>
)}
</div>
)}
</div>
</div>
{/* 控制按钮 */}
<div className="controls">
<button onClick={fetchData} disabled={isLoading}>
{isLoading ? 'Loading...' : 'Load Data'}
</button>
<button onClick={() => setError(null)}>Clear Error</button>
<button onClick={() => setData(null)}>Clear Data</button>
</div>
</div>
);
};
2.3.3 列表渲染
基础列表渲染:
import React, { useState } from 'react';
const ListRendering = () => {
const [items, setItems] = useState([
{ id: 1, name: 'Apple', price: 2.5, category: 'fruit' },
{ id: 2, name: 'Banana', price: 1.2, category: 'fruit' },
{ id: 3, name: 'Carrot', price: 0.8, category: 'vegetable' },
{ id: 4, name: 'Bread', price: 3.0, category: 'grain' },
]);
const [filter, setFilter] = useState('all');
// 过滤和排序逻辑
const filteredItems = items
.filter(item => filter === 'all' || item.category === filter)
.sort((a, b) => a.name.localeCompare(b.name));
return (
<div className="list-rendering">
<h2>List Rendering Examples</h2>
{/* 基础列表 */}
<div className="example">
<h3>1. Basic List</h3>
<ul>
{items.map(item => (
<li key={item.id}>
{item.name} - ${item.price}
</li>
))}
</ul>
</div>
{/* 带组件的列表 */}
<div className="example">
<h3>2. List with Components</h3>
<div className="item-grid">
{filteredItems.map(item => (
<ItemCard
key={item.id}
item={item}
onEdit={() => console.log('Edit:', item.id)}
onDelete={() => {
setItems(items.filter(i => i.id !== item.id));
}}
/>
))}
</div>
</div>
{/* 分组列表 */}
<div className="example">
<h3>3. Grouped List</h3>
<div className="grouped-items">
{['fruit', 'vegetable', 'grain'].map(category => {
const categoryItems = items.filter(item => item.category === category);
if (categoryItems.length === 0) return null;
return (
<div key={category} className="category-group">
<h4>{category.charAt(0).toUpperCase() + category.slice(1)}</h4>
<ul>
{categoryItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
})}
</div>
</div>
{/* 动态列表操作 */}
<div className="example">
<h3>4. Dynamic List Operations</h3>
<div className="filters">
<button
onClick={() => setFilter('all')}
className={filter === 'all' ? 'active' : ''}
>
All
</button>
<button
onClick={() => setFilter('fruit')}
className={filter === 'fruit' ? 'active' : ''}
>
Fruits
</button>
<button
onClick={() => setFilter('vegetable')}
className={filter === 'vegetable' ? 'active' : ''}
>
Vegetables
</button>
<button
onClick={() => setFilter('grain')}
className={filter === 'grain' ? 'active' : ''}
>
Grains
</button>
</div>
<button
onClick={() => {
const newItem = {
id: Date.now(),
name: `New Item ${items.length + 1}`,
price: Math.random() * 5 + 0.5,
category: 'fruit'
};
setItems([...items, newItem]);
}}
>
Add Item
</button>
</div>
{/* 嵌套列表 */}
<div className="example">
<h3>5. Nested List</h3>
<div className="nested-list">
{[
{
title: 'Fruits',
items: ['Apple', 'Banana', 'Orange']
},
{
title: 'Vegetables',
items: ['Carrot', 'Broccoli', 'Spinach']
},
{
title: 'Grains',
items: ['Wheat', 'Rice', 'Corn']
}
].map(category => (
<div key={category.title} className="category">
<h4>{category.title}</h4>
<ul>
{category.items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
))}
</div>
</div>
</div>
);
};
// 列表项组件
const ItemCard = ({ item, onEdit, onDelete }) => {
const [isHovered, setIsHovered] = useState(false);
return (
<div
className="item-card"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<h4>{item.name}</h4>
<p>Price: ${item.price.toFixed(2)}</p>
<p>Category: {item.category}</p>
{isHovered && (
<div className="actions">
<button onClick={onEdit}>Edit</button>
<button onClick={onDelete}>Delete</button>
</div>
)}
</div>
);
};
2.4 JSX 事件处理
2.4.1 事件绑定语法
基础事件处理:
import React, { useState } from 'react';
const EventHandling = () => {
const [count, setCount] = useState(0);
const [message, setMessage] = useState('');
const [isToggle, setIsToggle] = useState(false);
// 基础事件处理函数
const handleClick = () => {
setCount(count + 1);
console.log('Button clicked!');
};
// 带参数的事件处理
const handleIncrement = (amount) => {
setCount(count + amount);
};
// 使用箭头函数传递参数
const handleButtonClick = (value) => {
setMessage(`Button ${value} clicked!`);
};
// 表单事件处理
const handleInputChange = (e) => {
setMessage(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault(); // 阻止默认行为
alert(`Form submitted with: ${message}`);
};
// 键盘事件处理
const handleKeyPress = (e) => {
if (e.key === 'Enter') {
setMessage(`Enter pressed! Current count: ${count}`);
}
};
// 鼠标事件处理
const handleMouseEnter = () => {
setIsToggle(true);
};
const handleMouseLeave = () => {
setIsToggle(false);
};
return (
<div className="event-handling">
<h2>Event Handling Examples</h2>
{/* 基础点击事件 */}
<div className="example">
<h3>1. Basic Click Event</h3>
<button onClick={handleClick}>
Click Count: {count}
</button>
<button onClick={() => handleIncrement(5)}>
Add 5
</button>
<button onClick={() => handleIncrement(-1)}>
Subtract 1
</button>
</div>
{/* 表单事件 */}
<div className="example">
<h3>2. Form Events</h3>
<form onSubmit={handleSubmit}>
<input
type="text"
value={message}
onChange={handleInputChange}
onKeyPress={handleKeyPress}
placeholder="Type something..."
/>
<button type="submit">Submit</button>
</form>
{message && <p>Message: {message}</p>}
</div>
{/* 按钮组事件 */}
<div className="example">
<h3>3. Button Group Events</h3>
{['A', 'B', 'C'].map((value) => (
<button
key={value}
onClick={() => handleButtonClick(value)}
style={{ margin: '5px' }}
>
Button {value}
</button>
))}
</div>
{/* 鼠标事件 */}
<div className="example">
<h3>4. Mouse Events</h3>
<div
className={`hover-box ${isToggle ? 'active' : ''}`}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{
width: '200px',
height: '100px',
border: '2px solid #ccc',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: isToggle ? '#e0e0e0' : '#f5f5f5',
cursor: 'pointer'
}}
>
Hover over me!
</div>
{isToggle && <p>Mouse is over the box!</p>}
</div>
{/* 多种事件组合 */}
<div className="example">
<h3>5. Multiple Events</h3>
<div
onClick={() => console.log('Div clicked')}
onDoubleClick={() => console.log('Div double-clicked')}
onMouseDown={() => console.log('Mouse down')}
onMouseUp={() => console.log('Mouse up')}
style={{
width: '300px',
height: '150px',
border: '2px solid blue',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f0f8ff',
cursor: 'pointer'
}}
>
Click, double-click, or interact with this area
</div>
</div>
{/* 键盘事件 */}
<div className="example">
<h3>6. Keyboard Events</h3>
<div
tabIndex={0} // 使 div 可聚焦
onKeyDown={(e) => {
console.log('Key pressed:', e.key);
setMessage(`Key pressed: ${e.key}`);
}}
onKeyUp={(e) => {
console.log('Key released:', e.key);
}}
style={{
width: '300px',
height: '100px',
border: '2px solid green',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f0fff0'
}}
>
Press any key here
</div>
</div>
</div>
);
};
2.4.2 事件对象与参数
事件对象详解:
import React, { useState } from 'react';
const EventObjectDemo = () => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const [keyInfo, setKeyInfo] = useState({ key: '', code: '', ctrlKey: false });
const [formValue, setFormValue] = useState('');
// 鼠标事件处理
const handleMouseMove = (e) => {
setMousePosition({
x: e.clientX,
y: e.clientY
});
};
const handleMouseClick = (e) => {
console.log('Mouse click event:', e);
console.log('Target:', e.target);
console.log('Current target:', e.currentTarget);
console.log('Button:', e.button); // 0=左键, 1=中键, 2=右键
console.log('Coordinates:', { x: e.clientX, y: e.clientY });
};
// 键盘事件处理
const handleKeyDown = (e) => {
setKeyInfo({
key: e.key,
code: e.code,
ctrlKey: e.ctrlKey,
shiftKey: e.shiftKey,
altKey: e.altKey
});
// 阻止特定键的默认行为
if (e.key === 'F1') {
e.preventDefault();
alert('F1 key is disabled');
}
};
// 表单事件处理
const handleFormChange = (e) => {
const { name, value, type, checked } = e.target;
if (type === 'checkbox') {
setFormValue(checked);
} else {
setFormValue(value);
}
console.log('Form input change:', {
name,
value: type === 'checkbox' ? checked : value,
type
});
};
// 自定义事件参数传递
const handleAction = (action, value, event) => {
console.log('Action:', action);
console.log('Value:', value);
console.log('Event:', event);
// 访问事件对象
if (event) {
event.stopPropagation(); // 阻止事件冒泡
}
};
return (
<div className="event-object-demo" onMouseMove={handleMouseMove}>
<h2>Event Object Examples</h2>
{/* 鼠标事件对象 */}
<div className="example">
<h3>1. Mouse Event Object</h3>
<div
onClick={handleMouseClick}
style={{
width: '300px',
height: '150px',
border: '2px solid blue',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#e6f3ff',
cursor: 'pointer',
margin: '10px 0'
}}
>
Click here to see event details
</div>
<div style={{ padding: '10px', backgroundColor: '#f0f0f0' }}>
<strong>Mouse Position:</strong> X: {mousePosition.x}, Y: {mousePosition.y}
</div>
</div>
{/* 键盘事件对象 */}
<div className="example">
<h3>2. Keyboard Event Object</h3>
<input
type="text"
onKeyDown={handleKeyDown}
placeholder="Press any key"
style={{ padding: '8px', width: '300px' }}
/>
<div style={{ marginTop: '10px', padding: '10px', backgroundColor: '#f0f0f0' }}>
<strong>Key Info:</strong><br />
Key: {keyInfo.key}<br />
Code: {keyInfo.code}<br />
Ctrl: {keyInfo.ctrlKey ? 'Yes' : 'No'}<br />
Shift: {keyInfo.shiftKey ? 'Yes' : 'No'}
</div>
</div>
{/* 表单事件对象 */}
<div className="example">
<h3>3. Form Event Object</h3>
<div>
<label>
Text Input:
<input
type="text"
name="textInput"
value={typeof formValue === 'string' ? formValue : ''}
onChange={handleFormChange}
placeholder="Type something"
style={{ marginLeft: '10px', padding: '5px' }}
/>
</label>
</div>
<div style={{ marginTop: '10px' }}>
<label>
Checkbox:
<input
type="checkbox"
name="checkbox"
checked={typeof formValue === 'boolean' ? formValue : false}
onChange={handleFormChange}
style={{ marginLeft: '10px' }}
/>
Value: {formValue.toString()}
</label>
</div>
</div>
{/* 自定义参数传递 */}
<div className="example">
<h3>4. Custom Parameter Passing</h3>
{['Save', 'Delete', 'Edit'].map((action) => (
<button
key={action}
onClick={(e) => handleAction(action.toLowerCase(), `data-${action.toLowerCase()}`, e)}
style={{ margin: '5px', padding: '8px 16px' }}
>
{action}
</button>
))}
<div style={{ marginTop: '10px', padding: '10px', backgroundColor: '#f0f0f0' }}>
Check the console for event details
</div>
</div>
{/* 事件冒泡演示 */}
<div className="example">
<h3>5. Event Bubbling</h3>
<div
onClick={() => console.log('Outer div clicked')}
style={{
width: '300px',
height: '200px',
border: '3px solid red',
padding: '20px',
backgroundColor: '#ffe6e6'
}}
>
<p>Outer Div (Click me)</p>
<div
onClick={(e) => {
e.stopPropagation();
console.log('Inner div clicked (no bubbling)');
}}
style={{
width: '200px',
height: '100px',
border: '2px solid green',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#e6ffe6',
cursor: 'pointer'
}}
>
Inner Div (No Bubbling)
</div>
</div>
</div>
{/* 事件委托演示 */}
<div className="example">
<h3>6. Event Delegation</h3>
<div
onClick={(e) => {
if (e.target.tagName === 'BUTTON') {
const action = e.target.textContent;
console.log(`Delegated action: ${action}`);
}
}}
style={{ padding: '10px', border: '2px solid orange' }}
>
<p>Click any button (event delegation)</p>
{['Action 1', 'Action 2', 'Action 3'].map(action => (
<button key={action} style={{ margin: '5px' }}>
{action}
</button>
))}
</div>
</div>
</div>
);
};
2.4.3 高级事件处理模式
事件处理最佳实践:
import React, { useState, useCallback, useMemo, useRef } from 'react';
const AdvancedEventHandling = () => {
const [items, setItems] = useState([
{ id: 1, text: 'Item 1', completed: false },
{ id: 2, text: 'Item 2', completed: true },
{ id: 3, text: 'Item 3', completed: false },
]);
const [filter, setFilter] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
const inputRef = useRef(null);
// 使用 useCallback 优化事件处理函数
const handleToggleItem = useCallback((id) => {
setItems(prevItems =>
prevItems.map(item =>
item.id === id ? { ...item, completed: !item.completed } : item
)
);
}, []);
const handleDeleteItem = useCallback((id) => {
setItems(prevItems => prevItems.filter(item => item.id !== id));
}, []);
const handleAddItem = useCallback(() => {
if (inputRef.current && inputRef.current.value.trim()) {
const newItem = {
id: Date.now(),
text: inputRef.current.value.trim(),
completed: false
};
setItems(prevItems => [...prevItems, newItem]);
inputRef.current.value = '';
inputRef.current.focus();
}
}, []);
const handleKeyPress = useCallback((e) => {
if (e.key === 'Enter') {
handleAddItem();
}
}, [handleAddItem]);
// 使用 useMemo 优化计算
const filteredItems = useMemo(() => {
return items.filter(item => {
const matchesFilter =
filter === 'all' ||
(filter === 'completed' && item.completed) ||
(filter === 'active' && !item.completed);
const matchesSearch = item.text
.toLowerCase()
.includes(searchTerm.toLowerCase());
return matchesFilter && matchesSearch;
});
}, [items, filter, searchTerm]);
const stats = useMemo(() => ({
total: items.length,
completed: items.filter(item => item.completed).length,
active: items.filter(item => !item.completed).length
}), [items]);
// 防抖搜索
const debouncedSearch = useCallback(
debounce((value) => {
setSearchTerm(value);
}, 300),
[]
);
return (
<div className="advanced-event-handling">
<h2>Advanced Event Handling</h2>
{/* 统计信息 */}
<div className="stats">
<span>Total: {stats.total}</span>
<span>Active: {stats.active}</span>
<span>Completed: {stats.completed}</span>
</div>
{/* 添加项目 */}
<div className="add-item">
<input
ref={inputRef}
type="text"
onKeyPress={handleKeyPress}
placeholder="Add new item..."
/>
<button onClick={handleAddItem}>Add</button>
</div>
{/* 搜索和过滤 */}
<div className="filters">
<input
type="text"
onChange={(e) => debouncedSearch(e.target.value)}
placeholder="Search items..."
/>
<div className="filter-buttons">
{['all', 'active', 'completed'].map(filterType => (
<button
key={filterType}
onClick={() => setFilter(filterType)}
className={filter === filterType ? 'active' : ''}
>
{filterType.charAt(0).toUpperCase() + filterType.slice(1)}
</button>
))}
</div>
</div>
{/* 项目列表 */}
<div className="item-list">
{filteredItems.map(item => (
<TodoItem
key={item.id}
item={item}
onToggle={handleToggleItem}
onDelete={handleDeleteItem}
/>
))}
</div>
{/* 批量操作 */}
<div className="bulk-actions">
<button onClick={() => setItems(items.map(item => ({ ...item, completed: true })))}>
Complete All
</button>
<button onClick={() => setItems(items.map(item => ({ ...item, completed: false })))}>
Reset All
</button>
<button onClick={() => setItems(items.filter(item => !item.completed))}>
Clear Completed
</button>
</div>
</div>
);
};
// TodoItem 组件
const TodoItem = React.memo(({ item, onToggle, onDelete }) => {
const handleToggle = useCallback(() => {
onToggle(item.id);
}, [item.id, onToggle]);
const handleDelete = useCallback(() => {
onDelete(item.id);
}, [item.id, onDelete]);
return (
<div
className={`todo-item ${item.completed ? 'completed' : 'active'}`}
style={{
display: 'flex',
alignItems: 'center',
padding: '8px',
margin: '4px 0',
backgroundColor: item.completed ? '#f0f0f0' : 'white',
border: '1px solid #ddd',
borderRadius: '4px'
}}
>
<input
type="checkbox"
checked={item.completed}
onChange={handleToggle}
style={{ marginRight: '8px' }}
/>
<span
style={{
textDecoration: item.completed ? 'line-through' : 'none',
flex: 1,
color: item.completed ? '#888' : '#000'
}}
>
{item.text}
</span>
<button
onClick={handleDelete}
style={{
padding: '4px 8px',
backgroundColor: '#ff4444',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Delete
</button>
</div>
);
});
// 防抖函数
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
export default AdvancedEventHandling;
2.5 JSX 样式与 CSS
2.5.1 内联样式
内联样式基础:
import React, { useState } from 'react';
const InlineStyles = () => {
const [isHovered, setIsHovered] = useState(false);
const [theme, setTheme] = useState('light');
// 样式对象定义
const containerStyle = {
maxWidth: '800px',
margin: '0 auto',
padding: '20px',
backgroundColor: theme === 'light' ? '#ffffff' : '#1a1a1a',
color: theme === 'light' ? '#000000' : '#ffffff',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)'
};
const buttonStyle = {
padding: '12px 24px',
margin: '5px',
border: 'none',
borderRadius: '6px',
fontSize: '16px',
cursor: 'pointer',
transition: 'all 0.3s ease',
backgroundColor: '#007bff',
color: '#ffffff'
};
const hoverButtonStyle = {
...buttonStyle,
backgroundColor: isHovered ? '#0056b3' : '#007bff',
transform: isHovered ? 'translateY(-2px)' : 'translateY(0)',
boxShadow: isHovered ? '0 4px 12px rgba(0, 123, 255, 0.3)' : 'none'
};
const textStyle = {
fontSize: '18px',
lineHeight: '1.6',
marginBottom: '20px',
fontFamily: 'Arial, sans-serif'
};
const cardStyle = {
backgroundColor: theme === 'light' ? '#f8f9fa' : '#2d2d2d',
padding: '20px',
margin: '15px 0',
borderRadius: '8px',
border: `1px solid ${theme === 'light' ? '#dee2e6' : '#404040'}`,
transition: 'transform 0.2s ease'
};
return (
<div style={containerStyle}>
<h1 style={{ textAlign: 'center', marginBottom: '30px' }}>
Inline Styles Example
</h1>
{/* 基础内联样式 */}
<div>
<p style={textStyle}>
This paragraph uses inline styling with a style object.
</p>
{/* 动态样式 */}
<div style={cardStyle}>
<h3>Dynamic Styling</h3>
<p>Theme: {theme}</p>
<button
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
style={buttonStyle}
>
Toggle Theme
</button>
</div>
{/* 交互样式 */}
<div style={cardStyle}>
<h3>Interactive Styling</h3>
<div
style={hoverButtonStyle}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
Hover over me!
</div>
</div>
{/* 条件样式 */}
<div style={cardStyle}>
<h3>Conditional Styling</h3>
{['Success', 'Warning', 'Error', 'Info'].map((type) => {
const colors = {
Success: { bg: '#d4edda', color: '#155724' },
Warning: { bg: '#fff3cd', color: '#856404' },
Error: { bg: '#f8d7da', color: '#721c24' },
Info: { bg: '#d1ecf1', color: '#0c5460' }
};
return (
<div
key={type}
style={{
backgroundColor: colors[type].bg,
color: colors[type].color,
padding: '10px',
margin: '5px 0',
borderRadius: '4px',
border: `1px solid ${colors[type].color}`
}}
>
{type} Message
</div>
);
})}
</div>
{/* 计算样式 */}
<div style={cardStyle}>
<h3>Computed Styles</h3>
{[1, 2, 3, 4, 5].map((num) => (
<div
key={num}
style={{
width: `${num * 20}px`,
height: '30px',
backgroundColor: `hsl(${num * 60}, 70%, 50%)`,
margin: '5px',
borderRadius: '4px',
display: 'inline-block',
textAlign: 'center',
lineHeight: '30px',
color: 'white',
fontWeight: 'bold'
}}
>
{num}
</div>
))}
</div>
{/* 响应式样式模拟 */}
<div style={cardStyle}>
<h3>Responsive Styles (simulated)</h3>
<div
style={{
display: 'grid',
gridTemplateColumns: window.innerWidth > 768 ? 'repeat(3, 1fr)' : '1fr',
gap: '10px'
}}
>
{['Card 1', 'Card 2', 'Card 3'].map((text, index) => (
<div
key={index}
style={{
padding: '20px',
backgroundColor: theme === 'light' ? '#e9ecef' : '#404040',
borderRadius: '8px',
textAlign: 'center'
}}
>
{text}
</div>
))}
</div>
</div>
</div>
</div>
);
};
2.5.2 CSS 模块化
CSS Modules 使用:
// styles/Component.module.css
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.title {
color: #2c3e50;
font-size: 2rem;
text-align: center;
margin-bottom: 2rem;
}
.button {
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
}
.buttonPrimary {
background-color: #007bff;
color: white;
}
.buttonPrimary:hover {
background-color: #0056b3;
transform: translateY(-2px);
}
.buttonSecondary {
background-color: #6c757d;
color: white;
}
.buttonSecondary:hover {
background-color: #5a6268;
}
.card {
background-color: #f8f9fa;
padding: 20px;
margin: 15px 0;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.cardDark {
background-color: #2d2d2d;
border-color: #404040;
color: white;
}
/* 组合选择器 */
.container :global(.global-class) {
color: red;
}
/* 变量 */
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--success-color: #28a745;
--warning-color: #ffc107;
--error-color: #dc3545;
}
// components/StyledComponent.jsx
import React, { useState } from 'react';
import styles from '../styles/Component.module.css';
const StyledComponent = () => {
const [theme, setTheme] = useState('light');
const [isActive, setIsActive] = useState(false);
return (
<div className={styles.container}>
<h1 className={styles.title}>CSS Modules Example</h1>
{/* 基础样式使用 */}
<div className={styles.card}>
<h3>Basic CSS Modules</h3>
<p>This card uses CSS Modules for styling.</p>
<button className={styles.button}>
Basic Button
</button>
</div>
{/* 样式组合 */}
<div className={styles.card}>
<h3>Style Composition</h3>
<button className={`${styles.button} ${styles.buttonPrimary}`}>
Primary Button
</button>
<button className={`${styles.button} ${styles.buttonSecondary}`}>
Secondary Button
</button>
</div>
{/* 条件样式 */}
<div className={`${styles.card} ${theme === 'dark' ? styles.cardDark : ''}`}>
<h3>Conditional Styling</h3>
<p>Current theme: {theme}</p>
<button
className={`${styles.button} ${styles.buttonPrimary}`}
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
>
Toggle Theme
</button>
</div>
{/* 动态样式类 */}
<div className={styles.card}>
<h3>Dynamic Classes</h3>
<button
className={`${styles.button} ${isActive ? styles.buttonPrimary : styles.buttonSecondary}`}
onClick={() => setIsActive(!isActive)}
>
{isActive ? 'Active' : 'Inactive'}
</button>
</div>
{/* 使用 CSS 变量 */}
<div
className={styles.card}
style={{
'--primary-color': '#ff6b6b',
'--secondary-color': '#4ecdc4'
}}
>
<h3>CSS Variables</h3>
<div
className={styles.button}
style={{
backgroundColor: 'var(--primary-color)',
color: 'white'
}}
>
Custom Primary
</div>
<div
className={styles.button}
style={{
backgroundColor: 'var(--secondary-color)',
color: 'white'
}}
>
Custom Secondary
</div>
</div>
{/* 响应式样式 */}
<div className={`${styles.card} ${styles.responsiveCard}`}>
<h3>Responsive Design</h3>
<p>This card adapts to different screen sizes.</p>
</div>
</div>
);
};
export default StyledComponent;
2.5.3 CSS-in-JS 方案
使用 styled-components:
import React, { useState } from 'react';
import styled, { css, ThemeProvider, createGlobalStyle } from 'styled-components';
// 全局样式
const GlobalStyle = createGlobalStyle`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: ${props => props.theme.backgroundColor};
color: ${props => props.theme.textColor};
transition: all 0.3s ease;
}
`;
// 主题定义
const lightTheme = {
backgroundColor: '#ffffff',
textColor: '#333333',
primaryColor: '#007bff',
secondaryColor: '#6c757d',
successColor: '#28a745',
warningColor: '#ffc107',
errorColor: '#dc3545',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)'
};
const darkTheme = {
backgroundColor: '#1a1a1a',
textColor: '#ffffff',
primaryColor: '#0d6efd',
secondaryColor: '#6c757d',
successColor: '#198754',
warningColor: '#ffc107',
errorColor: '#dc3545',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(255, 255, 255, 0.1)'
};
// 样式组件定义
const Container = styled.div`
max-width: 800px;
margin: 0 auto;
padding: 2rem;
background-color: ${props => props.theme.backgroundColor};
border-radius: ${props => props.theme.borderRadius};
box-shadow: ${props => props.theme.boxShadow};
`;
const Title = styled.h1`
color: ${props => props.theme.textColor};
font-size: 2rem;
text-align: center;
margin-bottom: 2rem;
font-weight: 600;
`;
const Button = styled.button`
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 500;
${props => {
switch (props.variant) {
case 'primary':
return css`
background-color: ${props.theme.primaryColor};
color: white;
&:hover {
background-color: ${props.theme.primaryColor}dd;
transform: translateY(-2px);
box-shadow: 0 4px 12px ${props.theme.primaryColor}66;
}
`;
case 'secondary':
return css`
background-color: ${props.theme.secondaryColor};
color: white;
&:hover {
background-color: ${props.theme.secondaryColor}dd;
}
`;
case 'success':
return css`
background-color: ${props.theme.successColor};
color: white;
&:hover {
background-color: ${props.theme.successColor}dd;
}
`;
case 'warning':
return css`
background-color: ${props.theme.warningColor};
color: black;
&:hover {
background-color: ${props.theme.warningColor}dd;
}
`;
case 'error':
return css`
background-color: ${props.theme.errorColor};
color: white;
&:hover {
background-color: ${props.theme.errorColor}dd;
}
`;
default:
return css`
background-color: ${props.theme.primaryColor};
color: white;
`;
}
}}
${props => props.size === 'small' && css`
padding: 8px 16px;
font-size: 14px;
`}
${props => props.size === 'large' && css`
padding: 16px 32px;
font-size: 18px;
`}
${props => props.fullWidth && css`
width: 100%;
display: block;
`}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none !important;
}
`;
const Card = styled.div`
background-color: ${props => props.theme === 'dark' ? '#2d2d2d' : '#f8f9fa'};
padding: 20px;
margin: 15px 0;
border-radius: ${props => props.theme.borderRadius};
border: 1px solid ${props => props.theme === 'dark' ? '#404040' : '#dee2e6'};
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: ${props => props.theme === 'dark'
? '0 4px 20px rgba(255, 255, 255, 0.1)'
: '0 4px 20px rgba(0, 0, 0, 0.1)'
};
}
`;
const Input = styled.input`
width: 100%;
padding: 12px;
border: 2px solid ${props => props.theme === 'dark' ? '#404040' : '#dee2e6'};
border-radius: 6px;
font-size: 16px;
background-color: ${props => props.theme === 'dark' ? '#1a1a1a' : 'white'};
color: ${props => props.theme === 'dark' ? 'white' : 'black'};
transition: all 0.3s ease;
&:focus {
outline: none;
border-color: ${props => props.theme.primaryColor};
box-shadow: 0 0 0 3px ${props => props.theme.primaryColor}33;
}
&::placeholder {
color: ${props => props.theme === 'dark' ? '#888' : '#999'};
}
`;
const Grid = styled.div`
display: grid;
gap: 20px;
margin: 20px 0;
${props => props.columns === 2 && css`
grid-template-columns: repeat(2, 1fr);
`}
${props => props.columns === 3 && css`
grid-template-columns: repeat(3, 1fr);
`}
${props => props.columns === 4 && css`
grid-template-columns: repeat(4, 1fr);
`}
@media (max-width: 768px) {
grid-template-columns: 1fr;
}
`;
const CSSInJSExample = () => {
const [theme, setTheme] = useState('light');
const [inputValue, setInputValue] = useState('');
const [isLoading, setIsLoading] = useState(false);
const currentTheme = theme === 'light' ? lightTheme : darkTheme;
return (
<ThemeProvider theme={currentTheme}>
<GlobalStyle />
<Container>
<Title>CSS-in-JS with Styled Components</Title>
{/* 主题切换 */}
<Card>
<h3>Theme Switching</h3>
<p>Current theme: {theme}</p>
<Button
variant="primary"
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
>
Toggle Theme
</Button>
</Card>
{/* 按钮变体 */}
<Card>
<h3>Button Variants</h3>
<Grid columns={3}>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="success">Success</Button>
<Button variant="warning">Warning</Button>
<Button variant="error">Error</Button>
<Button
variant="primary"
disabled={isLoading}
onClick={() => {
setIsLoading(true);
setTimeout(() => setIsLoading(false), 2000);
}}
>
{isLoading ? 'Loading...' : 'Click Me'}
</Button>
</Grid>
</Card>
{/* 按钮尺寸 */}
<Card>
<h3>Button Sizes</h3>
<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
<Button variant="primary" size="small">Small</Button>
<Button variant="primary">Medium</Button>
<Button variant="primary" size="large">Large</Button>
<Button variant="primary" fullWidth>Full Width</Button>
</div>
</Card>
{/* 表单元素 */}
<Card>
<h3>Form Elements</h3>
<Input
type="text"
placeholder="Type something..."
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<p style={{ marginTop: '10px' }}>Input value: {inputValue}</p>
</Card>
{/* 动态样式 */}
<Card>
<h3>Dynamic Styling</h3>
{[1, 2, 3, 4, 5].map((num) => (
<Button
key={num}
variant="primary"
style={{
margin: '5px',
backgroundColor: `hsl(${num * 60}, 70%, 50%)`,
borderColor: `hsl(${num * 60}, 70%, 40%)`
}}
>
Color {num}
</Button>
))}
</Card>
{/* 响应式网格 */}
<Card>
<h3>Responsive Grid</h3>
<Grid columns={3}>
{['Card 1', 'Card 2', 'Card 3', 'Card 4', 'Card 5', 'Card 6'].map((text) => (
<Card key={text}>
<h4>{text}</h4>
<p>Responsive card content</p>
</Card>
))}
</Grid>
</Card>
</Container>
</ThemeProvider>
);
};
export default CSSInJSExample;
通过本章的 JSX 语法详解,你已经掌握了 React 开发的核心语法,包括 JSX 基础概念、表达式插值、事件处理、样式管理等关键内容。这些知识是构建 React 应用的基础,下一章我们将深入学习 React 组件系统。

浙公网安备 33010602011771号