组件基础 #
一、组件概述 #
1.1 什么是组件 #
组件是 Preact 应用的基本构建块,将 UI 拆分为独立、可复用的部分。
text
应用
├── Header 组件
├── Main 组件
│ ├── Sidebar 组件
│ └── Content 组件
└── Footer 组件
1.2 组件类型 #
| 类型 | 说明 | 推荐 |
|---|---|---|
| 函数组件 | 使用函数定义 | ✅ 推荐 |
| 类组件 | 使用类定义 | 兼容场景 |
二、函数组件 #
2.1 基本定义 #
jsx
// 函数声明
function Welcome() {
return <h1>Hello, Preact!</h1>;
}
// 箭头函数
const Welcome = () => <h1>Hello, Preact!</h1>;
// 导出组件
export function Welcome() {
return <h1>Hello, Preact!</h1>;
}
2.2 接收 Props #
jsx
function Greeting({ name, age }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Age: {age}</p>
</div>
);
}
// 使用
<Greeting name="Alice" age={25} />
2.3 默认 Props #
jsx
function Button({ text = 'Click me', type = 'button' }) {
return (
<button type={type}>
{text}
</button>
);
}
// 使用
<Button /> // text="Click me"
<Button text="Submit" /> // text="Submit"
2.4 Props 解构 #
jsx
function UserCard({
user: { name, email, avatar },
showEmail = true
}) {
return (
<div class="card">
<img src={avatar} alt={name} />
<h3>{name}</h3>
{showEmail && <p>{email}</p>}
</div>
);
}
2.5 children 属性 #
jsx
function Card({ children, title }) {
return (
<div class="card">
{title && <h2>{title}</h2>}
<div class="card-body">
{children}
</div>
</div>
);
}
// 使用
<Card title="Welcome">
<p>This is the card content.</p>
<button>Click me</button>
</Card>
2.6 完整示例 #
jsx
import { useState } from 'preact/hooks';
function Counter({ initialCount = 0, step = 1 }) {
const [count, setCount] = useState(initialCount);
return (
<div class="counter">
<h2>Counter: {count}</h2>
<button onClick={() => setCount(c => c - step)}>
-{step}
</button>
<button onClick={() => setCount(c => c + step)}>
+{step}
</button>
</div>
);
}
三、类组件 #
3.1 基本定义 #
jsx
import { Component } from 'preact';
class Welcome extends Component {
render() {
return <h1>Hello, Preact!</h1>;
}
}
3.2 使用 Props #
jsx
import { Component } from 'preact';
class Greeting extends Component {
render() {
const { name } = this.props;
return <h1>Hello, {name}!</h1>;
}
}
3.3 使用 State #
jsx
import { Component } from 'preact';
class Counter extends Component {
state = { count: 0 };
increment = () => {
this.setState({ count: this.state.count + 1 });
};
decrement = () => {
this.setState({ count: this.state.count - 1 });
};
render() {
return (
<div>
<h2>Count: {this.state.count}</h2>
<button onClick={this.decrement}>-</button>
<button onClick={this.increment}>+</button>
</div>
);
}
}
3.4 函数式 setState #
jsx
class Counter extends Component {
state = { count: 0 };
increment = () => {
this.setState(prevState => ({
count: prevState.count + 1
}));
};
render() {
return (
<div>
<p>{this.state.count}</p>
<button onClick={this.increment}>+</button>
</div>
);
}
}
3.5 类组件 vs 函数组件 #
jsx
// 类组件
class Toggle extends Component {
state = { on: false };
toggle = () => {
this.setState(prev => ({ on: !prev.on }));
};
render() {
return (
<button onClick={this.toggle}>
{this.state.on ? 'ON' : 'OFF'}
</button>
);
}
}
// 函数组件(推荐)
function Toggle() {
const [on, setOn] = useState(false);
return (
<button onClick={() => setOn(!on)}>
{on ? 'ON' : 'OFF'}
</button>
);
}
四、组件组合 #
4.1 组件嵌套 #
jsx
function App() {
return (
<div class="app">
<Header />
<Main>
<Sidebar />
<Content />
</Main>
<Footer />
</div>
);
}
function Header() {
return <header>Header</header>;
}
function Main({ children }) {
return <main>{children}</main>;
}
function Sidebar() {
return <aside>Sidebar</aside>;
}
function Content() {
return <section>Content</section>;
}
function Footer() {
return <footer>Footer</footer>;
}
4.2 组件作为 Props #
jsx
function Layout({ header, sidebar, content }) {
return (
<div class="layout">
<header>{header}</header>
<div class="body">
<aside>{sidebar}</aside>
<main>{content}</main>
</div>
</div>
);
}
// 使用
<Layout
header={<h1>My App</h1>}
sidebar={<nav>Navigation</nav>}
content={<article>Article content</article>}
/>
4.3 Render Props 模式 #
jsx
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return render({ data, loading });
}
// 使用
<DataFetcher
url="/api/users"
render={({ data, loading }) => (
loading
? <p>Loading...</p>
: <UserList users={data} />
)}
/>
五、组件类型 #
5.1 展示组件 #
jsx
// 只负责展示,不包含业务逻辑
function UserCard({ user }) {
return (
<div class="user-card">
<img src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
}
5.2 容器组件 #
jsx
// 负责数据获取和状态管理
function UserListContainer() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUsers().then(data => {
setUsers(data);
setLoading(false);
});
}, []);
if (loading) return <Loading />;
return <UserList users={users} />;
}
5.3 高阶组件 #
jsx
function withLoading(WrappedComponent) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) {
return <Loading />;
}
return <WrappedComponent {...props} />;
};
}
// 使用
const UserListWithLoading = withLoading(UserList);
<UserListWithLoading isLoading={loading} users={users} />
六、组件通信 #
6.1 父传子(Props) #
jsx
function Parent() {
const [message, setMessage] = useState('Hello');
return <Child message={message} />;
}
function Child({ message }) {
return <p>{message}</p>;
}
6.2 子传父(回调函数) #
jsx
function Parent() {
const [data, setData] = useState('');
const handleData = (newData) => {
setData(newData);
};
return (
<div>
<p>Received: {data}</p>
<Child onSend={handleData} />
</div>
);
}
function Child({ onSend }) {
return (
<button onClick={() => onSend('Hello from child')}>
Send Data
</button>
);
}
6.3 兄弟组件通信 #
jsx
function Parent() {
const [sharedData, setSharedData] = useState('');
return (
<div>
<SiblingA onData={setSharedData} />
<SiblingB data={sharedData} />
</div>
);
}
function SiblingA({ onData }) {
return (
<button onClick={() => onData('Data from A')}>
Send to B
</button>
);
}
function SiblingB({ data }) {
return <p>Received: {data}</p>;
}
七、组件命名规范 #
7.1 命名约定 #
jsx
// 组件名:PascalCase
function UserProfile() {}
function ShoppingCart() {}
// 文件名:PascalCase 或 kebab-case
// UserProfile.jsx 或 user-profile.jsx
// Props:camelCase
<UserProfile userName="Alice" isActive={true} />
// 事件处理函数:handle 前缀
const handleClick = () => {};
const handleSubmit = () => {};
7.2 组件组织 #
text
src/
├── components/
│ ├── common/ 通用组件
│ │ ├── Button.jsx
│ │ ├── Input.jsx
│ │ └── Modal.jsx
│ ├── layout/ 布局组件
│ │ ├── Header.jsx
│ │ ├── Footer.jsx
│ │ └── Sidebar.jsx
│ └── features/ 功能组件
│ ├── UserCard.jsx
│ └── ProductList.jsx
八、组件最佳实践 #
8.1 单一职责 #
jsx
// 好的做法:每个组件只做一件事
function UserName({ name }) {
return <span class="user-name">{name}</span>;
}
function UserAvatar({ url, alt }) {
return <img class="user-avatar" src={url} alt={alt} />;
}
function UserCard({ user }) {
return (
<div class="user-card">
<UserAvatar url={user.avatar} alt={user.name} />
<UserName name={user.name} />
</div>
);
}
8.2 保持组件简单 #
jsx
// 避免:过于复杂的组件
function BadComponent({ data, config, user, theme }) {
// 100+ 行代码...
}
// 推荐:拆分为小组件
function GoodComponent({ data }) {
return (
<div>
<Header />
<Content data={data} />
<Footer />
</div>
);
}
8.3 提取可复用逻辑 #
jsx
// 自定义 Hook
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// 使用
function ResponsiveComponent() {
const { width } = useWindowSize();
return (
<div>
{width < 768 ? <MobileView /> : <DesktopView />}
</div>
);
}
九、总结 #
| 要点 | 说明 |
|---|---|
| 函数组件 | 推荐使用,更简洁 |
| Props | 只读,父传子 |
| children | 组件内容插槽 |
| 组合 | 组件嵌套和复用 |
| 通信 | Props 向下,回调向上 |
函数组件优势:
- 代码更简洁
- 没有 this 绑定问题
- 更容易测试
- 更好的性能
最后更新:2026-03-28