React学习之组件体系

58 阅读3分钟

组件组合(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

使用建议

  1. 优先使用 children:适合简单的内容透传。
  2. 复杂布局用具名 props:如 headeractionsfooter 等,语义清晰。
  3. 需要数据共享时用 render prop 或函数 children:实现灵活的作用域控制。
  4. 避免过度抽象:不是所有组件都需要插槽,保持 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.”

译:“只有几行代码的组件不仅没问题,反而受到鼓励。“