R04第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

组件组合与 children

组件组合与 children · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

这一课解决「容器和内容怎么复用」:children prop 让组件把「内容」当参数收进来。读完你能实现 Card、Section 这类容器组件,并判断什么时候该用组合、而不是继续给组件加开关属性。

问题场景

页面上要三种卡片:普通卡片、带头部的卡片、带底部按钮的卡片。第一版做法是给 Card 不断加开关:

function Card({ showHeader, headerText, showFooter, footerText, children }) {
  return (
    <div className="card">
      {showHeader && <header>{headerText}</header>}
      {children}
      {showFooter && <footer>{footerText}</footer>}
    </div>
  );
}

四个开关还不够——产品接着要「头部带关闭按钮」「底部放两个按钮」。每来一种新需求就加一个 prop,组件内部的条件分支越堆越多,最后没人敢动它。

核心解释

children 是组件标签之间的内容,本质上是一个 prop。 写 <Card><h3>标题</h3></Card> 时,中间那段 JSX 会作为 children 传进组件,想放哪就渲染到哪:

function Card({ children }) {
  return <div className="card">{children}</div>;
}

组合的思路是「结构写在使用方,能力写在容器里」。卡片要头部?在使用方直接写:<Card><CardHeader title="..."/><p>正文</p></Card>。容器只负责统一的边框、间距、布局——它不知道也不关心里面是什么。这与函数设计的道理相同:让调用方传内容,比让组件枚举所有内容形态更可扩展。

多槽位布局也一样,children 只是一个特殊的 prop,其他 prop 同样可以传 JSX:

function Layout({ sidebar, content }) {
  return (
    <div className="row">
      <aside>{sidebar}</aside>
      <main>{content}</main>
    </div>
  );
}

什么时候用组合而不是加 prop?给个判断标准:新需求改变的是「内容或结构」时,用组合;改变的是「外观或行为的一个具体维度」时,加一个 prop 没问题。一旦发现自己在写 showHeader、showFooter、headerVariant="with-close" 这类开关,就该停下来换组合了。

示例拆解

实验区提供一个「开关地狱」对照组和一个组合实现,关键点如下:

  • CardWithFlags:靠 showHeader、showFooter、footerText 控制,演示区有一个「加需求」按钮,每点一次开关数量 +1,直观感受 prop 爆炸;
  • Card:只收 children,头部、正文、底部全由使用方拼装,容器代码永远三行;
  • 使用方 <Card><h4>...</h4><p>...</p><button>...</button></Card> 展示了内容的自由度——放什么、放几个都行;
  • Layout 演示多槽位:sidebar 与 content 两个 JSX prop 各归其位。

验证方式:两种实现的渲染结果可以做到一模一样,但 CardWithFlags 每加一种形态要改组件本身,组合版只改使用方——改动隔离在外,容器保持稳定。

动手任务

打开本课实验区,完成三个小改动:

  1. 用 Card 组合出一张「带头部标题和底部按钮」的卡片,全程不改 Card 本身。
  2. 给 Layout 的侧边栏和主区各换一种内容,体会槽位的灵活性。
  3. 把 CardWithFlags 里的开关再减一半,看看剩下的分支还剩多少可读性。

常见错误

  • 忘记渲染 children:容器收了 children 却没在 JSX 里输出 {children},使用方写了内容但页面空白。
  • 把所有东西都做成 prop:text、html、headerNode、footerNode 传一堆 JSX prop,不如直接用 children 清晰。
  • children 当普通变量用:它是 JSX(React 元素),不能直接 .map 或改属性;要循环渲染请让使用方传数组。
  • 容器插手内容逻辑:容器里写 if (children 某文本) 做特殊处理,等于把开关逻辑换了个地方藏;容器只管布局和样式。

检查与复盘

完成下方检查题确认理解。自查清单:

  • 能说出 children 的本质:一个内容为 JSX 的 prop
  • 设计组件前先问「新需求动的是内容还是外观」,再决定用组合还是加 prop
  • 审查组件时,看到成堆的 show 开关就能识别出「该组合了」

参考资料

动手实验
动手实验
预览
点击「运行」在隔离沙箱中预览
检查题共 2 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1单选简单

关于 <Card><p>正文</p></Card> 中的 children,正确的说法是?

Q2读代码中等

阅读代码(React 18+)。页面上最终渲染出的完整文本是什么?

function Panel({ title, children }) {
  return (
    <section>
      <h3>{title}</h3>
      <div>{children}</div>
    </section>
  );
}

export default function App() {
  return (
    <Panel title='周报'>
      <p>完成题库 30 题</p>
      <p>修复 3 个 bug</p>
    </Panel>
  );
}

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...