Appearance
组件的导入与使用
学习目标
通过本章学习,你将掌握:
- React组件的各种导入方式
- 组件使用的基本语法和高级技巧
- Props传递的多种方式
- 组件组合与嵌套
- React 19的新特性使用
- 性能优化与最佳实践
第一部分:组件导入基础
1.1 导入默认导出的组件
基本语法
jsx
// Button.jsx
export default function Button({ children }) {
return <button>{children}</button>;
}
// App.jsx
import Button from './Button';
function App() {
return <Button>点击我</Button>;
}自定义导入名称
jsx
// 可以使用任意名称导入默认导出
import MyButton from './Button';
import Btn from './Button';
import PrimaryButton from './Button';
// 所有这些都是同一个组件
<MyButton>按钮1</MyButton>
<Btn>按钮2</Btn>
<PrimaryButton>按钮3</PrimaryButton>导入路径规则
jsx
// 1. 相对路径
import Button from './Button'; // 同目录
import Button from '../Button'; // 上级目录
import Button from '../../components/Button'; // 上两级
// 2. 绝对路径(配置别名后)
import Button from '@/components/Button';
import Button from '@components/Button';
// 3. node_modules包
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
// 4. 省略文件扩展名(webpack/vite会自动解析)
import Button from './Button'; // 自动查找 Button.jsx, Button.js, Button.tsx
import Button from './Button.jsx'; // 明确指定扩展名1.2 导入命名导出的组件
基本语法
jsx
// FormControls.jsx
export function Input({ value, onChange }) {
return <input value={value} onChange={onChange} />;
}
export function Select({ options, value, onChange }) {
return (
<select value={value} onChange={onChange}>
{options.map(opt => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
);
}
// App.jsx - 导入命名导出
import { Input, Select } from './FormControls';
function App() {
const [name, setName] = useState('');
return (
<>
<Input value={name} onChange={e => setName(e.target.value)} />
<Select options={options} value={value} onChange={onChange} />
</>
);
}导入时重命名
jsx
// 导入并重命名
import { Input as TextInput, Select as Dropdown } from './FormControls';
function App() {
return (
<>
<TextInput value={name} onChange={handleChange} />
<Dropdown options={options} value={value} onChange={handleSelect} />
</>
);
}
// 避免命名冲突
import { Button } from './OldComponents';
import { Button as NewButton } from './NewComponents';
<Button>旧按钮</Button>
<NewButton>新按钮</NewButton>批量导入
jsx
// 导入所有命名导出
import * as Forms from './FormControls';
function App() {
return (
<>
<Forms.Input value={name} onChange={handleChange} />
<Forms.Select options={options} value={value} onChange={handleSelect} />
<Forms.Checkbox checked={checked} onChange={handleCheck} />
</>
);
}1.3 混合导入
jsx
// Card.jsx
export default function Card({ children }) {
return <div className="card">{children}</div>;
}
export function CardHeader({ children }) {
return <div className="card-header">{children}</div>;
}
export function CardBody({ children }) {
return <div className="card-body">{children}</div>;
}
// App.jsx - 同时导入默认和命名导出
import Card, { CardHeader, CardBody } from './Card';
function App() {
return (
<Card>
<CardHeader>
<h2>标题</h2>
</CardHeader>
<CardBody>
<p>内容</p>
</CardBody>
</Card>
);
}1.4 动态导入(懒加载)
jsx
import { lazy, Suspense } from 'react';
// 动态导入组件
const Dashboard = lazy(() => import('./Dashboard'));
const UserProfile = lazy(() => import('./UserProfile'));
const Settings = lazy(() => import('./Settings'));
function App() {
const [currentPage, setCurrentPage] = useState('dashboard');
return (
<div>
<nav>
<button onClick={() => setCurrentPage('dashboard')}>仪表板</button>
<button onClick={() => setCurrentPage('profile')}>个人资料</button>
<button onClick={() => setCurrentPage('settings')}>设置</button>
</nav>
<Suspense fallback={<div>加载中...</div>}>
{currentPage === 'dashboard' && <Dashboard />}
{currentPage === 'profile' && <UserProfile />}
{currentPage === 'settings' && <Settings />}
</Suspense>
</div>
);
}条件动态导入
jsx
// 根据条件决定导入哪个组件
function App() {
const [showNewUI, setShowNewUI] = useState(false);
// 动态选择要导入的组件
const Component = lazy(() =>
showNewUI
? import('./NewDashboard')
: import('./OldDashboard')
);
return (
<Suspense fallback={<Loading />}>
<Component />
</Suspense>
);
}第二部分:组件使用方式
2.1 基本使用
自闭合组件
jsx
// 无子元素的组件使用自闭合标签
<Button />
<Input />
<Image src="..." alt="..." />
<Divider />
// 等价于
<Button></Button>
<Input></Input>包含子元素的组件
jsx
// 有子元素时必须有闭合标签
<Button>
点击我
</Button>
<Card>
<h2>标题</h2>
<p>内容</p>
</Card>
// 错误示例
<Button>点击我 {/* 缺少闭合标签 */}2.2 Props传递
基本Props
jsx
// 字符串props(可以不用花括号)
<Button text="点击" color="primary" />
<Button text={'点击'} color={'primary'} />
// 其他类型props必须用花括号
<Counter initialCount={0} />
<UserCard user={userData} />
<TodoList items={todoItems} />
// 布尔值props
<Button disabled={true} />
<Button disabled /> // true的简写
<Input required={false} />
<Input required={!isOptional} />对象和数组Props
jsx
// 对象props
const user = {
name: 'Alice',
age: 25,
email: 'alice@example.com'
};
<UserCard user={user} />
// 内联对象(注意双花括号)
<UserCard user={{ name: 'Alice', age: 25 }} />
// 数组props
const items = ['Item 1', 'Item 2', 'Item 3'];
<List items={items} />
// 内联数组
<List items={['Item 1', 'Item 2', 'Item 3']} />函数Props
jsx
// 函数props(事件处理器)
function App() {
const handleClick = () => {
console.log('Clicked!');
};
const handleSubmit = (data) => {
console.log('Submitted:', data);
};
return (
<>
<Button onClick={handleClick} />
<Form onSubmit={handleSubmit} />
{/* 内联函数(不推荐,每次渲染创建新函数) */}
<Button onClick={() => console.log('Clicked!')} />
{/* 传递参数 */}
<Button onClick={() => handleDelete(item.id)} />
</>
);
}Props展开
jsx
// 使用展开运算符传递所有props
const props = {
text: '点击',
color: 'primary',
size: 'large',
disabled: false
};
<Button {...props} />
// 等价于
<Button
text="点击"
color="primary"
size="large"
disabled={false}
/>
// 覆盖某些props
<Button {...props} disabled={true} /> // disabled会覆盖props中的值
// 先展开再覆盖 vs 先覆盖再展开
<Button disabled={true} {...props} /> // props中的disabled会覆盖true
<Button {...props} disabled={true} /> // true会覆盖props中的disabled2.3 Children的使用
基本Children
jsx
// Card组件接收children
function Card({ children }) {
return <div className="card">{children}</div>;
}
// 使用
<Card>
<h2>标题</h2>
<p>内容</p>
</Card>
// children可以是任何类型
<Card>Hello World</Card> // 字符串
<Card>{123}</Card> // 数字
<Card>
<Component /> // 组件
</Card>
<Card>
{items.map(item => <li key={item.id}>{item.text}</li>)} // 数组
</Card>多个Children
jsx
// Layout组件接收多个部分
function Layout({ header, sidebar, content, footer }) {
return (
<div className="layout">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{content}</main>
<footer>{footer}</footer>
</div>
);
}
// 使用
<Layout
header={<Header />}
sidebar={<Sidebar />}
content={<MainContent />}
footer={<Footer />}
/>Render Props
jsx
// DataProvider使用render prop
function DataProvider({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url).then(r => r.json()).then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return render({ data, loading });
}
// 使用
<DataProvider
url="/api/users"
render={({ data, loading }) => (
loading ? <Spinner /> : <UserList users={data} />
)}
/>2.4 组件组合
组合模式
jsx
// 1. 简单组合
function UserDashboard() {
return (
<div>
<Header />
<Sidebar />
<MainContent />
<Footer />
</div>
);
}
// 2. 条件组合
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<div>
{isLoggedIn ? (
<>
<Dashboard />
<UserProfile />
</>
) : (
<LoginForm />
)}
</div>
);
}
// 3. 列表组合
function TodoApp() {
const [todos, setTodos] = useState([]);
return (
<div>
<TodoInput onAdd={handleAdd} />
<TodoList>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={handleToggle}
onDelete={handleDelete}
/>
))}
</TodoList>
</div>
);
}复合组件模式
jsx
// Tabs组件族
function Tabs({ children, defaultIndex = 0 }) {
const [activeIndex, setActiveIndex] = useState(defaultIndex);
return (
<div className="tabs">
{React.Children.map(children, (child, index) =>
React.cloneElement(child, {
isActive: index === activeIndex,
onClick: () => setActiveIndex(index)
})
)}
</div>
);
}
function Tab({ label, isActive, onClick, children }) {
return (
<>
<button className={isActive ? 'active' : ''} onClick={onClick}>
{label}
</button>
{isActive && <div className="tab-content">{children}</div>}
</>
);
}
// 使用
<Tabs defaultIndex={0}>
<Tab label="标签1">内容1</Tab>
<Tab label="标签2">内容2</Tab>
<Tab label="标签3">内容3</Tab>
</Tabs>第三部分:React 19新特性使用
3.1 use() Hook使用
jsx
import { use, Suspense } from 'react';
// 创建Promise
function fetchUser(id) {
return fetch(`/api/users/${id}`).then(r => r.json());
}
// 使用use()消费Promise
function UserProfile({ userId }) {
const user = use(fetchUser(userId));
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// 必须包裹在Suspense中
function App() {
return (
<Suspense fallback={<div>加载中...</div>}>
<UserProfile userId="123" />
</Suspense>
);
}条件使用use()(React 19新特性)
jsx
function ConditionalData({ shouldFetch, userId }) {
// React 19允许条件使用use()
const user = shouldFetch ? use(fetchUser(userId)) : null;
return (
<div>
{user ? (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
) : (
<p>未获取数据</p>
)}
</div>
);
}3.2 Server Components使用
jsx
// Server Component(默认)
async function UserList() {
// 直接在组件中await数据
const users = await db.users.findAll();
return (
<ul>
{users.map(user => (
<li key={user.id}>
<UserCard user={user} />
</li>
))}
</ul>
);
}
// Client Component
'use client';
function UserCard({ user }) {
const [isExpanded, setIsExpanded] = useState(false);
return (
<div>
<h3>{user.name}</h3>
{isExpanded && <p>{user.email}</p>}
<button onClick={() => setIsExpanded(!isExpanded)}>
{isExpanded ? '收起' : '展开'}
</button>
</div>
);
}
// 在页面中使用
function Page() {
return (
<div>
<h1>用户列表</h1>
<Suspense fallback={<Loading />}>
<UserList />
</Suspense>
</div>
);
}3.3 Server Actions使用
jsx
// Server Action
'use server';
export async function createTodo(formData) {
const text = formData.get('text');
const todo = await db.todos.create({ text });
revalidatePath('/todos');
return todo;
}
// Client Component使用Server Action
'use client';
import { createTodo } from './actions';
import { useActionState } from 'react';
function TodoForm() {
const [state, formAction, isPending] = useActionState(createTodo, null);
return (
<form action={formAction}>
<input name="text" required />
<button type="submit" disabled={isPending}>
{isPending ? '添加中...' : '添加'}
</button>
{state && <p>已添加: {state.text}</p>}
</form>
);
}3.4 useOptimistic使用
jsx
'use client';
import { useOptimistic } from 'react';
function TodoList({ todos, addTodo }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo) => [...state, newTodo]
);
async function handleSubmit(formData) {
const newTodo = {
id: Date.now(),
text: formData.get('text'),
completed: false
};
// 乐观更新UI
addOptimisticTodo(newTodo);
// 实际提交到服务器
await addTodo(newTodo);
}
return (
<div>
<ul>
{optimisticTodos.map(todo => (
<li key={todo.id}>
{todo.text}
{!todos.find(t => t.id === todo.id) && (
<span className="pending">正在保存...</span>
)}
</li>
))}
</ul>
<form action={handleSubmit}>
<input name="text" />
<button>添加</button>
</form>
</div>
);
}3.5 useFormStatus使用
jsx
'use client';
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending, data, method, action } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? '提交中...' : '提交'}
</button>
);
}
function MyForm() {
async function handleSubmit(formData) {
'use server';
await saveData(formData);
}
return (
<form action={handleSubmit}>
<input name="username" />
<SubmitButton />
</form>
);
}第四部分:高级使用技巧
4.1 高阶组件使用
jsx
// 高阶组件定义
function withAuth(Component) {
return function AuthenticatedComponent(props) {
const { user, loading } = useAuth();
if (loading) return <Spinner />;
if (!user) return <Navigate to="/login" />;
return <Component {...props} user={user} />;
};
}
// 使用高阶组件
function Dashboard({ user }) {
return <div>Welcome, {user.name}</div>;
}
const AuthenticatedDashboard = withAuth(Dashboard);
// 在App中使用
<AuthenticatedDashboard />组合多个HOC
jsx
// 定义多个HOC
const withAuth = (Component) => (props) => { /* ... */ };
const withLoading = (Component) => (props) => { /* ... */ };
const withErrorBoundary = (Component) => (props) => { /* ... */ };
// 方式1:嵌套使用
const EnhancedComponent = withAuth(withLoading(withErrorBoundary(MyComponent)));
// 方式2:使用compose函数
function compose(...funcs) {
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
const enhance = compose(withAuth, withLoading, withErrorBoundary);
const EnhancedComponent = enhance(MyComponent);
// 使用
<EnhancedComponent />4.2 Render Props使用
jsx
// Render Props组件
function Mouse({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMouseMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);
return render(position);
}
// 使用方式1:render prop
<Mouse render={({ x, y }) => (
<div>鼠标位置: {x}, {y}</div>
)} />
// 使用方式2:children as function
function Mouse({ children }) {
// ... 同上
return children(position);
}
<Mouse>
{({ x, y }) => <div>鼠标位置: {x}, {y}</div>}
</Mouse>4.3 Context使用
jsx
// 创建Context
const ThemeContext = createContext('light');
const UserContext = createContext(null);
// Provider
function App() {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState(null);
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={user}>
<Dashboard />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
// 消费Context
function Dashboard() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<div className={theme}>
Welcome, {user?.name}
</div>
);
}
// React 19可以用use()
function Dashboard() {
const theme = use(ThemeContext);
const user = use(UserContext);
return (
<div className={theme}>
Welcome, {user?.name}
</div>
);
}4.4 Fragment使用
jsx
// 方式1:使用<Fragment>
import { Fragment } from 'react';
function Table() {
return (
<table>
<tbody>
<Fragment>
<tr><td>Row 1</td></tr>
<tr><td>Row 2</td></tr>
</Fragment>
</tbody>
</table>
);
}
// 方式2:使用<>...</>(短语法)
function Table() {
return (
<table>
<tbody>
<>
<tr><td>Row 1</td></tr>
<tr><td>Row 2</td></tr>
</>
</tbody>
</table>
);
}
// 带key的Fragment(必须使用完整语法)
function List({ items }) {
return (
<>
{items.map(item => (
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
))}
</>
);
}4.5 Portals使用
jsx
import { createPortal } from 'react-dom';
// Modal组件
function Modal({ children, onClose }) {
return createPortal(
<div className="modal-backdrop" onClick={onClose}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
{children}
</div>
</div>,
document.getElementById('modal-root') // 渲染到body下的modal-root
);
}
// 使用
function App() {
const [showModal, setShowModal] = useState(false);
return (
<div>
<button onClick={() => setShowModal(true)}>打开模态框</button>
{showModal && (
<Modal onClose={() => setShowModal(false)}>
<h2>模态框内容</h2>
<p>这是通过Portal渲染的</p>
</Modal>
)}
</div>
);
}
// index.html需要有
// <div id="root"></div>
// <div id="modal-root"></div>第五部分:性能优化
5.1 避免不必要的重新渲染
jsx
// 问题:父组件更新导致子组件重新渲染
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
<ExpensiveChild /> {/* 每次Parent更新都会重新渲染 */}
</div>
);
}
// 解决方案1:React.memo
const ExpensiveChild = React.memo(function ExpensiveChild() {
console.log('ExpensiveChild rendered');
return <div>Expensive component</div>;
});
// 解决方案2:children提升
function Parent({ children }) {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
{children} {/* children不会重新创建 */}
</div>
);
}
<Parent>
<ExpensiveChild />
</Parent>
// 解决方案3:组件拆分
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
function Parent() {
return (
<div>
<Counter /> {/* 状态隔离 */}
<ExpensiveChild />
</div>
);
}5.2 优化Props传递
jsx
// 问题:每次渲染创建新的对象/函数
function Parent() {
return (
<Child
style={{ padding: 20 }} // 每次都是新对象
onClick={() => console.log('clicked')} // 每次都是新函数
/>
);
}
// 解决方案1:提取到组件外部
const style = { padding: 20 };
function Parent() {
return <Child style={style} onClick={handleClick} />;
}
function handleClick() {
console.log('clicked');
}
// 解决方案2:useMemo和useCallback
function Parent() {
const style = useMemo(() => ({ padding: 20 }), []);
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
return <Child style={style} onClick={handleClick} />;
}
// React 19 Compiler会自动优化
function Parent() {
// 编译器自动添加memo
return (
<Child
style={{ padding: 20 }}
onClick={() => console.log('clicked')}
/>
);
}5.3 列表渲染优化
jsx
// 正确使用key
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id} // 使用唯一且稳定的key
todo={todo}
/>
))}
</ul>
);
}
// 虚拟滚动(大列表)
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>{items[index].text}</div>
);
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}5.4 懒加载优化
jsx
// 路由级别懒加载
const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));
const ContactPage = lazy(() => import('./pages/ContactPage'));
function App() {
return (
<Router>
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/contact" element={<ContactPage />} />
</Routes>
</Suspense>
</Router>
);
}
// 组件级别懒加载
function Dashboard() {
const [showChart, setShowChart] = useState(false);
const Chart = lazy(() => import('./Chart'));
return (
<div>
<button onClick={() => setShowChart(true)}>显示图表</button>
{showChart && (
<Suspense fallback={<div>加载图表...</div>}>
<Chart />
</Suspense>
)}
</div>
);
}第六部分:常见问题与解决
6.1 组件未渲染问题
jsx
// 问题1:组件名小写
function myComponent() { // 错误!
return <div>Hello</div>;
}
<myComponent /> // 被当作HTML标签
// 解决:组件名必须大写
function MyComponent() {
return <div>Hello</div>;
}
<MyComponent />
// 问题2:忘记导出
// Button.jsx
function Button() {
return <button>Click</button>;
}
// 忘记: export default Button;
// 解决:添加导出
export default function Button() {
return <button>Click</button>;
}
// 问题3:导入路径错误
import Button from './button'; // 错误:文件名大小写
import Button from './Button'; // 正确6.2 Props未传递问题
jsx
// 问题:忘记传递必需的props
function UserCard({ user }) {
return <div>{user.name}</div>; // user可能是undefined
}
<UserCard /> // 错误:未传递user
// 解决1:传递props
<UserCard user={userData} />
// 解决2:设置默认值
function UserCard({ user = { name: 'Guest' } }) {
return <div>{user.name}</div>;
}
// 解决3:添加类型检查(PropTypes或TypeScript)
import PropTypes from 'prop-types';
UserCard.propTypes = {
user: PropTypes.object.isRequired
};6.3 事件处理器问题
jsx
// 问题:直接调用函数而不是传递引用
<button onClick={handleClick()}>Click</button> // 错误:立即执行
// 解决1:传递函数引用
<button onClick={handleClick}>Click</button>
// 解决2:使用箭头函数(传递参数时)
<button onClick={() => handleClick(param)}>Click</button>
// 问题:类组件中this丢失
class MyComponent extends Component {
handleClick() {
this.setState({ ... }); // this是undefined
}
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}
// 解决1:箭头函数
class MyComponent extends Component {
handleClick = () => {
this.setState({ ... });
};
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}
// 解决2:bind
class MyComponent extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ ... });
}
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}6.4 异步数据加载问题
jsx
// 问题:组件卸载后仍然setState
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser); // 组件卸载后仍执行
}, [userId]);
return <div>{user?.name}</div>;
}
// 解决1:使用cleanup函数
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let isMounted = true;
fetchUser(userId).then(data => {
if (isMounted) {
setUser(data);
}
});
return () => {
isMounted = false;
};
}, [userId]);
return <div>{user?.name}</div>;
}
// 解决2:使用AbortController
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${userId}`, { signal: controller.signal })
.then(r => r.json())
.then(setUser)
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});
return () => controller.abort();
}, [userId]);
return <div>{user?.name}</div>;
}
// 解决3:React 19的use() Hook
function UserProfile({ userId }) {
const user = use(fetchUser(userId));
return <div>{user.name}</div>;
}第七部分:最佳实践总结
7.1 组件导入建议
jsx
// 1. 导入顺序
// React核心
import React, { useState, useEffect } from 'react';
// 第三方库
import { BrowserRouter, Route } from 'react-router-dom';
import axios from 'axios';
// 本地组件
import Button from './components/Button';
import Input from './components/Input';
// 工具函数
import { formatDate } from './utils';
// 样式
import './App.css';
// 2. 分组导入
import {
Button,
Input,
Select,
Checkbox
} from './components/forms';
// 3. 使用路径别名
import Button from '@/components/Button';
import { formatDate } from '@/utils/date';7.2 组件使用建议
jsx
// 1. 保持组件简洁
// 不好
function App() {
// 100行代码...
return (
<div>
{/* 复杂的JSX */}
</div>
);
}
// 好:拆分为多个小组件
function App() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
);
}
// 2. Props传递清晰
// 不好
<UserCard {...user} {...settings} {...handlers} />
// 好
<UserCard
user={user}
settings={settings}
onEdit={handleEdit}
onDelete={handleDelete}
/>
// 3. 合理使用children
// 不好
<Card header={<h2>Title</h2>} body={<p>Content</p>} />
// 好
<Card>
<CardHeader>
<h2>Title</h2>
</CardHeader>
<CardBody>
<p>Content</p>
</CardBody>
</Card>7.3 性能优化建议
jsx
// 1. 避免内联对象和函数
// 不好
<Component style={{ margin: 10 }} onClick={() => {}} />
// 好
const style = { margin: 10 };
const handleClick = useCallback(() => {}, []);
<Component style={style} onClick={handleClick} />
// 2. 正确使用key
// 不好
{items.map((item, index) => <Item key={index} {...item} />)}
// 好
{items.map(item => <Item key={item.id} {...item} />)}
// 3. 使用React.memo
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
// 复杂渲染逻辑
return <div>{/* ... */}</div>;
});
// 4. 懒加载大组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>练习题
基础练习
- 创建一个App组件,导入并使用3个不同的子组件
- 实现组件间的props传递(父到子、子到父)
- 使用Fragment和Portal渲染组件
进阶练习
- 实现一个使用HOC的权限控制系统
- 创建一个使用Render Props的数据获取组件
- 使用Context实现主题切换功能
高级练习
- 使用React 19的use() Hook实现数据获取
- 创建一个Server Component和Client Component混合的应用
- 实现一个使用useOptimistic的乐观更新表单
通过本章学习,你已经全面掌握了React组件的导入与使用技巧。合理使用这些技术,能让你的React应用更加高效、易维护。继续实践,成为React高手!