children 与 slots
React 把嵌套内容作为 props.children 传递;Vue 通过默认、具名和作用域插槽组织内容出口。
核心结论
内容分发让容器组件负责结构,调用方负责具体内容。React 的嵌套 JSX 最终进入 children prop;Vue 用默认、具名和作用域 slot 把内容出口变成模板级协议。
组合流程
组件关系与数据流
ComponentContent OutletContent
差异焦点内容是父组件创建的 React 元素对象,并随 props 一起传递。
实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。
flowchart TB
accTitle: React children 与元素 prop
accDescr: ReactDemo 在每次 render 中创建正文和按钮 React 元素,分别作为 children 与 footer prop 传给 ProfileCard,子组件直接把 props 放进自己的 JSX。
parent["ReactDemo render"] --> elements[("创建 React elements")]
elements -->|"children prop"| card["ProfileCard"]
elements -->|"footer prop"| card
card --> output[("组合后的元素树")]
output -.->|"按钮事件更新父 state"| parent
class parent,card component
class elements,output resource
classDef state fill:#fffdf8,stroke:#149eca,color:#25221d,stroke-width:1.2px
classDef component fill:#e4f5fa,stroke:#149eca,color:#25221d,stroke-width:2px,font-weight:600
classDef process fill:#f4fafb,stroke:#149eca,color:#25221d,stroke-width:1.6px
classDef resource fill:#eee9df,stroke:#817a70,color:#25221d,stroke-width:1.4px嵌套 JSX 最终也是 children prop,具名区域通常再设计一个普通 prop。
组件关系与数据流
ComponentContent OutletContent
差异焦点插槽是模板级内容出口,具名和作用域能力由框架直接提供。
实线表示依赖、数据或命令传递,虚线表示事件、回调或清理路径。
flowchart TB
accTitle: Vue 默认与具名插槽
accDescr: VueDemo 模板为 default 和 footer 提供内容,编译器生成 slot functions,ProfileCard 渲染到 slot outlet 时调用对应函数。
parent["VueDemo template"] --> slots[("default / footer slot functions")]
slots --> outlet{{"ProfileCard 调用 slot outlets"}}
outlet --> card["ProfileCard template"]
card --> output[("组合后的 VNodes")]
output -.->|"按钮事件修改父 ref"| parent
class parent,card component
class outlet process
class slots,output resource
classDef state fill:#fffdf8,stroke:#2f9d6b,color:#25221d,stroke-width:1.2px
classDef component fill:#e4f4eb,stroke:#2f9d6b,color:#25221d,stroke-width:2px,font-weight:600
classDef process fill:#f2f8f4,stroke:#2f9d6b,color:#25221d,stroke-width:1.6px
classDef resource fill:#eee9df,stroke:#817a70,color:#25221d,stroke-width:1.4px父模板提供插槽函数,子组件通过默认或具名 slot outlet 决定插入位置。
完整 Demo 源码
以下代码直接读取在线 Demo 使用的源文件,没有省略或改写。
React 源码
import { useState } from "react";import { ProfileCard } from "./ProfileCard";export function ReactDemo() { const [completed, setCompleted] = useState(3); return ( <ProfileCard title="本周学习进度" footer={ <button className="demo-button" type="button" onClick={() => setCompleted((current) => current + 1)} > 完成一篇 </button> } > <p>已经完成 {completed} 篇 React / Vue 对比笔记。</p> <p className="demo-muted">正文作为 children,按钮作为 footer prop。</p> </ProfileCard> );}Vue 3 源码
<script setup lang="ts">import { ref } from "vue";import ProfileCard from "./ProfileCard.vue";const completed = ref(3);</script><template> <ProfileCard title="本周学习进度"> <p>已经完成 {{ completed }} 篇 React / Vue 对比笔记。</p> <p class="demo-muted">正文进入默认插槽,按钮进入 footer 具名插槽。</p> <template #footer> <button class="demo-button" type="button" @click="completed += 1"> 完成一篇 </button> </template> </ProfileCard></template>在线观察
两个父组件向同一个卡片结构传入正文和底部操作。
React
本周学习进度
已经完成 3 篇 React / Vue 对比笔记。
正文作为 children,按钮作为 footer prop。
Vue 3
本周学习进度
已经完成 3 篇 React / Vue 对比笔记。
正文进入默认插槽,按钮进入 footer 具名插槽。
什么时候使用
| 需求 | React | Vue 3 |
|---|---|---|
| 单个主要内容区 | children | 默认 slot |
| 多个具名区域 | 元素 prop,如 footer | 具名 slot |
| 子组件把数据交给内容模板 | render prop | 作用域 slot |
内容分发解决“谁提供 UI”,普通数据和事件仍使用组件通信。