插槽vue/react
作用域插槽(Scoped Slots) 是 Vue 提供的一种让插槽内容可以访问子组件内部数据的机制,它极大地增强了组件的灵活性与复用性,是 Vue 组件设计中非常强大的功能之一。
一、vue插槽
先回顾:普通插槽(默认插槽、具名插槽)
在 Vue 中,插槽(slot)是父组件向子组件传递一段模板内容(HTML / 组件)的机制,用于实现内容分发。比如:
<!-- 子组件 Child.vue -->
<template><div><slot></slot> <!-- 普通插槽 --></div>
</template><!-- 父组件 -->
<Child><p>这是插入到子组件中的内容</p> <!-- 父组件传的内容 -->
</Child>
✅ 这是普通插槽,父组件传什么,子组件就在 <slot>
的位置显示什么。
那什么是作用域插槽呢?
作用域插槽(Scoped Slot) 是一种特殊的插槽,它允许父组件在插入内容时,可以访问子组件内部的数据。
作用域插槽的核心作用,就是让子组件可以把内部的数据“暴露”给父组件,让父组件可以基于这些数据,自由地决定如何渲染插槽内容。
换句话说:
通常插槽是父 → 子 传内容;
但作用域插槽是子 → 父 传数据,让父组件决定如何渲染这些数据。
🔧 作用域插槽的语法(Vue 2 & Vue 3)
✅ Vue 2 中的作用域插槽语法
在子组件中,给 <slot>
绑定数据:
<!-- 子组件 Child.vue -->
<template><div><slot :user="user" :items="items"></slot><!-- 把 user 和 items 数据暴露给父组件插槽内容 --></div>
</template><script>
export default {data() {return {user: { name: 'Alice', age: 25 },items: ['Apple', 'Banana', 'Cherry']};}
};
</script>
在父组件中,通过 v-slot(或 slot-scope) 接收子组件传递的数据:
<!-- 父组件 -->
<template><Child><!-- Vue 2 写法1:slot-scope(旧版) --><template slot-scope="{ user, items }"><p>用户名:{{ user.name }}</p><ul><li v-for="item in items" :key="item">{{ item }}</li></ul></template></Child>
</template>
或者 Vue 2 也支持新版语法(推荐,和 Vue 3 类似):
<Child><template #default="{ user, items }"><p>{{ user.name }}</p><ul><li v-for="item in items" :key="item">{{ item }}</li></ul></template>
</Child>
✅ Vue 3 中的作用域插槽语法(更统一、更简洁)
在子组件中,同样通过 <slot :data="xxx">
传递数据:
<!-- 子组件 Child.vue -->
<template><div><slot :user="user" :items="items"></slot></div>
</template><script setup>
import { ref } from 'vue'const user = ref({ name: 'Alice', age: 25 });
const items = ref(['Apple', 'Banana', 'Cherry']);
</script>
在父组件中,使用 v-slot 或 # 语法接收:
<template><Child><template #default="{ user, items }"><p>用户:{{ user.name }}, 年龄:{{ user.age }}</p><ul><li v-for="item in items" :key="item">{{ item }}</li></ul></template></Child>
</template>
✅ 推荐使用
#default="{ ... }"
这种简洁写法(Vue 3)
📌 作用域插槽的典型应用场景(附例子)
✅ 场景 1:列表组件(父组件自定义每一项的展示)
子组件:ItemList.vue
<template><ul><li v-for="(item, index) in items" :key="index"><!-- 把 item 数据通过作用域插槽暴露出去 --><slot :item="item" :index="index"></slot></li></ul>
</template><script setup>
defineProps(['items'])
</script>
父组件:
<template><ItemList :items="data"><template #default="{ item, index }"><span>第 {{ index }} 项:{{ item.name }}</span></template></ItemList>
</template><script setup>
import ItemList from './ItemList.vue'const data = [{ name: 'Apple' },{ name: 'Banana' },{ name: 'Cherry' }
]
</script>
🔍 效果: 子组件负责循环渲染列表,但每一项显示什么内容,由父组件通过插槽自定义!
✅ 场景 2:卡片组件(父组件自定义卡片内容)
子组件:Card.vue
<template><div class="card"><slot :title="title" :content="content"></slot></div>
</template><script setup>
defineProps(['title', 'content'])
</script>
父组件:
<template><Card :title="'Hello'" :content="'Vue is awesome'"><template #default="{ title, content }"><h3>{{ title }}</h3><p>{{ content }}</p></template></Card>
</template>
你问的是:“React 怎么实现类似 Vue 的插槽(slot)的功能?”
这是一个非常重要且实用的跨框架对比问题!🎯
在 Vue 中,插槽(slot) 是一种非常核心的组件内容分发机制,它让父组件可以向子组件传递一段模板内容(UI 片段),从而实现高复用、灵活定制的组件设计。
而 React 本身没有直接叫“插槽”的概念,但完全可以通过 React 的组件设计模式 + props(特别是 children 和 render props) 轻松实现 类似 Vue 插槽的功能,甚至更灵活!
二、🔧 2. React 中如何实现类似 Vue “插槽” 的功能?
React 没有 Vue 那样的 <slot>
标签,但可以通过 props.children
实现默认插槽,通过命名 props(如 header、main)实现具名插槽,通过函数 props(如 renderXXX)实现作用域插槽,从而达到与 Vue 插槽几乎相同的组件内容分发与定制能力,而且同样灵活强大!
✅ 方法一:使用 children
props(实现默认插槽)
React 中,父组件传递给子组件的 JSX 内容,可以通过
props.children
获取!
这相当于 Vue 的 默认插槽。
📌 示例:React 实现默认插槽(类似 Vue 的 <slot>
)
子组件:Child.js
function Child(props) {return (<div><h2>这是子组件</h2>{/* React 中通过 props.children 获取父组件传入的内容 */}{props.children}</div>);
}export default Child;
父组件:App.js
import Child from './Child';function App() {return (<Child><p>🎉 这段内容就是插槽内容,会显示在子组件里的 {props.children} 位置</p></Child>);
}
🔍 效果:
父组件在 <Child>
标签内写的内容,会通过 props.children
渲染到子组件中,就像 Vue 的 <slot>
一样。
✅ 这就是 React 实现 Vue 默认插槽的方式!
✅ 方法二:实现类似 Vue 的具名插槽
在 Vue 中,你可以有多个具名插槽,比如:
<template><div><slot name="header"></slot><slot></slot><slot name="footer"></slot></div>
</template>
父组件可以这样传:
<Child><template v-slot:header>这是头部</template><p>这是默认内容</p><template v-slot:footer>这是页脚</template>
</Child>
✅ React 中如何实现具名插槽?
React 没有 v-slot:name
,但可以通过 传递对象形式的 children 或命名 props 来模拟!
推荐方式:通过 props 传入不同区块内容(模拟具名插槽)
子组件:Child.js
function Child({ header, main, footer }) {return (<div>{header && <header>{header}</header>}<main>{main}</main>{footer && <footer>{footer}</footer>}</div>);
}export default Child;
父组件:App.js
import Child from './Child';function App() {return (<Childheader={<h1>这是头部(类似具名插槽 header)</h1>}main={<p>这是主要内容(类似默认插槽)</p>}footer={<footer>这是页脚(类似具名插槽 footer)</footer>}/>);
}
🔍 说明:
不像 Vue 那样用
<template v-slot:name>
,而是通过 props 传入不同区块的 JSX比如:
header={<h1>...</h1>}
、main={<p>...</p>}
、footer={...}
这是 React 社区中非常常见且清晰的实现具名插槽的方式 ✅
✅ 方法三:更灵活的方式 —— 使用 Render Props(高级用法)
Render Props 是一种 React 设计模式,允许你通过一个函数 prop,把渲染逻辑交给父组件控制,从而实现更灵活的“插槽”或内容分发。
不过,对于大多数“插槽”场景,children
或 命名 props 已经足够,Render Props 更适合控制渲染逻辑,而非单纯的内容分发。
但为了完整性,简单介绍一下:
子组件:
function SlotDemo({ renderHeader, renderContent }) {return (<div>{renderHeader && renderHeader()}<main>{renderContent && renderContent()}</main></div>);
}
父组件:
<SlotDemorenderHeader={() => <h1>这是通过函数传入的头部</h1>}renderContent={() => <p>这是通过函数传入的内容</p>}
/>
🔒 适用场景更复杂,比如子组件控制何时渲染、如何传参等,但代码可读性稍差,一般推荐上面 children / props 方式。
三、🔍 3. 对比 Vue 插槽 和 React 实现方式
功能 | Vue 插槽 | React 实现方式 |
---|---|---|
默认插槽 |
| 通过 |
具名插槽 |
| 通过 props 传入不同 JSX 块: |
作用域插槽 |
| 通过 props 传数据给父组件传入的函数或组件,比如 |
灵活度 | 高,语法清晰 | 同样高,但语法习惯不同(更灵活、更 JS 风格) |