关于Vue 2.x 中 slots 和 $scopedSlots 的本质区别。下面我们来详细解释一下并从源码和实现角度深入分析它们之间的关系。
✅ 正确认识:$scopedSlots 包含作用域插槽和普通插槽,而 $slots 只包含普通插槽
📌 在 Vue 2.x 中:
$slots是一个对象,每个属性对应一个编译后的 VNode(虚拟节点),表示该名称的插槽内容。$scopedSlots是一个函数映射对象,每个属性是一个返回 VNode 的函数,可以接收子组件传递的数据。
🔍 关键点:
Vue 2.x 中,所有插槽都会被处理为
$scopedSlots中的函数形式,但为了向后兼容,$slots仅包含那些没有使用作用域的插槽,并将它们编译为静态 VNode。
🧠 举个例子说明:
<!-- Child.vue -->
<template>
<div>
<slot></slot>
<slot name="default"></slot>
<slot name="header" :user="user"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: 'Tom'
};
}
};
</script>
<!-- Parent.vue -->
<template>
<Child>
<span>默认插槽内容</span>
<template #header="{ user }">
<p>欢迎你,{{ user }}</p>
</template>
</Child>
</template>
在 Child 组件内部访问:
this.$slots.default; // VNode 数组,表示默认插槽的内容(不带作用域)
this.$slots.header; // undefined,因为它是作用域插槽
this.$scopedSlots.default; // function (props) => VNode
this.$scopedSlots.header; // function (props) => VNode
🧭 所以我们可以这样总结:
| 插槽类型 | 是否存在于 $slots | 是否存在于 $scopedSlots | 是否能接收数据 |
|---|---|---|---|
| 普通插槽 | ✅ | ✅ | ❌ |
| 作用域插槽 | ❌ | ✅ | ✅ |
因此,
$scopedSlots包含了所有的插槽定义(无论是不是作用域插槽),而$slots只包含那些没有使用作用域的插槽。
⚠️ 注意事项:
-
在 Vue 3 中,
$slots和$scopedSlots被统一成$slots,且所有插槽都以函数形式存在:this.$slots.default?.(); // 默认插槽调用 this.$slots.default?.({ user }); // 如果有传参 -
Vue 2.x 中仍需区分
$slots和$scopedSlots,但建议尽量使用$scopedSlots来统一处理插槽逻辑,避免遗漏作用域插槽。
✅ 实际开发建议:
- 如果你在编写可复用组件,尤其是希望支持高度定制化时,推荐统一使用
$scopedSlots。 - 在 Vue 2.x 中,检查某个插槽是否存在时,优先使用
this.$scopedSlots[name]。 - 避免直接操作
$slots,除非你明确知道插槽是静态内容。
📚 总结一句话:
在 Vue 2.x 中,
$scopedSlots包含了所有类型的插槽(包括普通插槽和作用域插槽),而$slots只包含普通插槽;因此可以说$scopedSlots是$slots的“超集”。
如果你正在封装组件库或高阶组件,理解这一层关系非常重要,它能帮助你写出更健壮、灵活的组件插槽逻辑。