目录
-
- [一、React children 与 Vue slot 全面对比](#一、React children 与 Vue slot 全面对比)
- [二、从 Vue slot 的视角来理解 React children](#二、从 Vue slot 的视角来理解 React children)
-
- [1、Vue 的默认插槽 - 基本没有本质区别(React children)](#1、Vue 的默认插槽 - 基本没有本质区别(React children))
- [2、Vue 的具名插槽 - React 使用普通 Props(React children)](#2、Vue 的具名插槽 - React 使用普通 Props(React children))
- [3、Vue 的作用域插槽 - 两者都能实现子组件向插槽内容提供数据(React children)](#3、Vue 的作用域插槽 - 两者都能实现子组件向插槽内容提供数据(React children))
- [三、为什么 React 不专门设计一个 Slot API?](#三、为什么 React 不专门设计一个 Slot API?)
- 四、最终总结
React 的 children 与 Vue 的 slot,核心设计思想基本相同,都是通过 组件组合 (Composition),让父组件向子组件提供内容,由子组件决定这些内容的渲染位置。
但二者有一个重要区别:
Vue 将 Slot 设计成专门的模板插槽机制,而 React 没有独立的 Slot API,而是通过
children和普通 Props 传递 React 节点实现类似能力。
总得来讲,React 的 children 比 Vue 的 slot 更简约大气一些,更符合"大道至简"的路子。
一、React children 与 Vue slot 全面对比
| 对比维度 | React children / Props |
Vue slot |
|---|---|---|
| 核心定位 | 基于 Props 的组件组合机制 | 专门用于组件内容分发的插槽机制 |
| 核心思想 | 组件接收外部传入的内容,并决定其渲染位置 | 组件声明插槽位置,父组件提供插槽内容 |
| 默认插槽 | children |
<slot /> |
| 具名插槽 | 普通 Props,如 header、footer |
<slot name="header" /> |
| 插槽内容传递 | 传递 React 节点(JSX 等) | 传递模板片段,底层以插槽函数表示 |
| 作用域插槽 | 通过 Render Props(函数 Props)实现 | 通过 Scoped Slots 实现 |
| 内容作用域 | JSX 通常在父组件的词法作用域中创建 | 插槽内容通常在父组件的模板作用域中求值 |
| 子组件向内容提供数据 | 调用函数 Props 并传入参数 | 通过 <slot :data="..."> 暴露 Slot Props |
| 默认内容 | 通过 JS 默认值、条件判断实现 | 直接在 <slot> 内声明后备内容 |
| 多区域组合 | 多个 JSX Props | 多个具名 Slot |
| 类型约束 | TypeScript 定义 Props 类型 | defineSlots() 等机制 |
| 灵活性 | JSX、函数和 Props 组合非常自由 | 模板语义明确,具名和作用域插槽更直观 |
| 主要特点 | 统一、灵活、偏 JavaScript 组合 | 声明式、语义清晰、偏模板组合 |
二、从 Vue slot 的视角来理解 React children
1、Vue 的默认插槽 - 基本没有本质区别(React children)
React:
ts
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
<Card>
<h2>标题</h2>
<p>内容</p>
</Card>
Vue:
html
<!-- Card.vue -->
<template>
<div class="card">
<slot />
</div>
</template>
<!-- 使用 -->
<Card>
<h2>标题</h2>
<p>内容</p>
</Card>
两者效果相同:父组件提供内容,Card 组件决定内容插入的位置。
2、Vue 的具名插槽 - React 使用普通 Props(React children)
React:
ts
function Layout({
header,
footer,
children
}) {
return (
<>
<header>{header}</header>
<main>{children}</main>
<footer>{footer}</footer>
</>
);
}
<Layout
header={<Header />}
footer={<Footer />}
>
<Content />
</Layout>
Vue:
html
<!-- Layout.vue -->
<template>
<header>
<slot name="header" />
</header>
<main>
<slot />
</main>
<footer>
<slot name="footer" />
</footer>
</template>
<!-- 使用 -->
<Layout>
<template #header>
<Header />
</template>
<Content />
<template #footer>
<Footer />
</template>
</Layout>
这里 Vue 的优势是具有专门的插槽语法,而 React 直接利用 JSX 作为 JavaScript 值的能力。
3、Vue 的作用域插槽 - 两者都能实现子组件向插槽内容提供数据(React children)
这一点是最值得理解的。
- 普通插槽解决的是:父组件向子组件传递什么内容?
- 作用域插槽进一步解决:父组件提供的内容,如何使用子组件内部的数据?
React:Render Props
ts
function List({ items, renderItem }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>
{renderItem(item)}
</li>
))}
</ul>
);
}
<List
items={users}
renderItem={user => (
<strong>{user.name}</strong>
)}
/>
Vue:Scoped Slots
ts
<!-- List.vue -->
<template>
<ul>
<li
v-for="item in items"
:key="item.id"
>
<slot :item="item" />
</li>
</ul>
</template>
<!-- 使用 -->
<List :items="users">
<template #default="{ item }">
<strong>
{{ item.name }}
</strong>
</template>
</List>
React 通过调用函数 renderItem(item) 将数据交给父组件定义的渲染逻辑;Vue 通过 slot props 实现类似的数据传递。
React 也可以将 children 本身定义为函数,形成 Function as Children 模式。
三、为什么 React 不专门设计一个 Slot API?
Vue Slot 是一套专门的内容分发 API;React children 是利用通用 Props 实现内容组合的一种惯用方式。
主要是两种框架的表达方式不同:
- Vue 是模板驱动的框架:需要通过
<slot>、v-slot、#header等专门语法,表达内容的插入位置及数据关系。 - React 使用 JSX 作为 JavaScript 表达式:JSX 可以赋值给变量、通过 Props 传递,也可以由函数返回。因此,无须引入专门的 Slot 语法,也能完成内容组合。
这不代表 React 比 Vue 更先进,或者 Vue 的 Slot 是多余设计。两者只是 API 设计取向不同。
四、最终总结
| Vue | React 对应机制 |
|---|---|
默认插槽 slot |
children |
具名插槽 slot name |
JSX Props |
作用域插槽 v-slot |
Render Props / Function as Children |
| 插槽后备内容 | 默认值或条件渲染 |
| 动态插槽名 | 动态选择 Props 或组合结构 |
| 插槽内容复用 | JSX 变量、组件组合、渲染函数 |
如果从框架设计思想上理解:
- Vue 倾向于把组件组合能力封装成专门的模板语法;
- React 倾向于利用 JavaScript 本身的表达能力实现组件组合。