组件组合(Composition)
组件组合(Composition) 是一种核心的设计思想和最佳实践,指的是通过将多个简单、可复用的组件“组合”在一起,来构建更复杂 UI 的方式。它替代了传统面向对象编程中的“继承(Inheritance)”,是 React 官方强烈推荐的代码组织方式。
常见模式
1. 通过 children 组合内容(最基础)
类似vue-默认插槽
1.1 React demo
父组件将子内容作为 children 传给通用容器组件。
// 通用容器
const Panel = ({ children }) => (
<div className="panel">
<div className="panel-body">{children}</div>
</div>
);
// 使用
<Panel>
<h2>欢迎</h2>
<p>这是组合的内容!</p>
</Panel>
1.2 vue demo
这里类似 vue 中的默认插槽
// 组件button
<template>
<button class="n-button">
<slot></slot>
</button>
</template>
// 使用button
import NButton from '@/components/n-button.vue';
<template>
<div>
<NButton>
Button 1
</NButton>
</div>
</template>
2. 通过 props 传递子组件(具名组合)
类似vue中的 具名插槽
2.1 react demo
const Dialog = ({ title, content, actions }) => (
<div className="dialog">
<header>{title}</header>
<main>{content}</main>
<footer>{actions}</footer>
</div>
);
// 使用
<Dialog
title={<h3>确认删除?</h3>}
content={<p>此操作不可逆。</p>}
actions={
<>
<button>取消</button>
<button className="danger">删除</td>
</>
}
/>
2.2 vue demo
vue具名插槽
// BaseLayout 组件
<template>
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
// 使用
<BaseLayout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</BaseLayout>
3. 作用域插槽(Scoped Slots)
Vue 的作用域插槽允许子组件向父组件暴露数据。在 React 中,可以通过 函数作为 children(render props) 或 函数类型的 prop 实现。
3.1 react demo
函数作为children
// 父级页面
import List from './list'
const ChildrenDemoPage = () => {
return (
<div>
<h1>Children Demo Page</h1>
<List>
{(item: string) => <span style={{ color: 'blue' }}>{item}</span>}
</List>
</div>
);
}
export default ChildrenDemoPage;
// 子组件
const List = ({ children }: any) => {
const items = ['Item 1', 'Item 2', 'Item 3'];
return (
<ul>
{items.map((item, index) =>
<li key={index}>
{typeof children === 'function' ? children(item) : children}
</li>
)}
</ul>
);
};
export default List;
函数类型props实现:
// 父组件
import List from './list'
const ChildrenDemoPage = () => {
return (
<div>
<h1>Children Demo Page</h1>
<List
renderItem={(item: string) => <span style={{ color: 'blue' }}>{item}</span>}
/>
</div>
);
}
export default ChildrenDemoPage;
// 子组件
const List = ({ renderItem }: any) => {
const items = ['render Item 1', 'render Item 2', 'render Item 3'];
return (
<ul>
{items.map((item, index) =>
<li key={index}>
{typeof renderItem === 'function' ? renderItem(item) : renderItem}
</li>
)}
</ul>
);
};
export default List;
3.2 vue demo
<!-- 子组件 -->
<template>
<button class="n-button">
<slot name="header" :message="headerTitle"></slot>
<slot></slot>
<slot name="footer" :message="footerMessage"></slot>
</button>
</template>
<script setup lang="ts">
// 你的其他逻辑
const headerTitle = 'This is the header title';
const footerMessage = 'This is the footer message';
</script>
<!-- 父组件,使用 -->
<template>
<NButton>
<template #default>
<div>
<p>Main Content Area</p>
</div>
</template>
<template #header="{ message }">
<strong>{{ message }}</strong>
</template>
<template #footer="{ message }">
<em>{{ message }}</em>
</template>
</NButton>
</template>
4. 自定义 Hook + 组合(现代 React 推荐)🌟
将逻辑抽离到自定义 Hook,UI 仍通过组合构建。
// 自定义 Hook
function useMousePosition() {
const [pos, setPos] = useState({ x: 0, y: 0 });
const handleMove = (e) => setPos({ x: e.clientX, y: e.clientY });
return [pos, handleMove];
}
// 组合使用
const App = () => {
const [mouse, handleMove] = useMousePosition();
return (
<div onMouseMove={handleMove}>
<Display coords={mouse} /> {/* 纯展示组件 */}
</div>
);
};
Hook 负责逻辑,组件负责组合和渲染。
React 组合 vs Vue 插槽
| 功能 | Vue 插槽语法 | React 实现方式 |
|---|---|---|
| 默认插槽 | <slot /> | props.children |
| 具名插槽 | <slot name="header" /> | 通过 props 传入 JSX(如 header={...}) |
| 作用域插槽 | <slot :item="item" /> | 函数作为 children 或 render prop |
使用建议
- 优先使用
children:适合简单的内容透传。 - 复杂布局用具名 props:如
header、actions、footer等,语义清晰。 - 需要数据共享时用 render prop 或函数 children:实现灵活的作用域控制。
- 避免过度抽象:不是所有组件都需要插槽,保持 API 简洁。
使用场景
| 场景 | 组合方式 |
|---|---|
| 布局系统 | Layout + Header/Sidebar/Main |
| 表单 | Form + Input/Select/SubmitButton |
| 卡片组件 | Card + CardHeader/CardBody/CardFooter |
| 弹窗/模态框 | Modal + 自定义 title/body/actions |
| 列表渲染 | List + ListItem 或 render prop |
| 表格渲染 | Table |
总结
-
组件组合是 React 的灵魂:一切皆组件,通过组合构建复杂 UI。
-
核心工具:
children、props 传 JSX、函数子组件、自定义 Hook。 -
设计原则:
- 小而专一的组件
- 通过组合而非继承扩展功能
- 逻辑(Hook)与 UI(组件)分离
正如 React 官方所说: “Components that are only a few lines long are not only okay, they’re encouraged.”
译:“只有几行代码的组件不仅没问题,反而受到鼓励。“