$scopedSlots(作用域插槽)
定义:子组件提供数据,父组件决定如何渲染,数据作用域属于子组件。
本质:子组件不直接渲染内容,而是接收一个函数,这个函数在子组件作用域内执行,从而让父组件的模板可以访问子组件的数据。
this.$scopedSlots.default = function(data) {
return VNode
}
$slots(普通插槽)
定义:父组件提供内容,子组件决定在哪里渲染,数据作用域属于父组件。
本质:父组件在编译时就已经确定了插槽内容的所有数据和逻辑,子组件只是作为一个容器来摆放这些内容。
this.$slots.default = [VNode, VNode, ...]
数据作用域的指向
<template>
<div>
<ChildComponent>
<div>{{ parentMessage }}</div>
</ChildComponent>
<ChildComponent>
<template v-slot:default="slotProps">
<div>{{ parentMessage }}</div>
<div>{{ slotProps.childMessage }}</div>
</template>
</ChildComponent>
</div>
</template>
<script>
export default {
data() {
return {
parentMessage: '父组件的数据'
}
}
}
</script>
<template>
<div>
<slot></slot>
<slot :childMessage="childMessage"></slot>
</div>
</template>
<script>
export default {
data() {
return {
childMessage: '子组件的数据'
}
}
}
</script>
编译时 vs 运行时
普通插槽:编译时确定
<template>
<child>
<span>{{ message }}</span>
</child>
</template>
render() {
const children = [createVNode('span', null, this.message)]
return h(Child, null, { default: () => children })
}
作用域插槽:运行时确定
<template>
<child>
<template v-slot="props">
<span>{{ props.message }}</span>
</template>
</child>
</template>
render() {
const scopedSlotFn = (props) => {
return createVNode('span', null, props.message)
}
return h(Child, null, { default: scopedSlotFn })
}
render() {
const vnode = this.$scopedSlots.default({ message: this.childMessage })
return vnode
}
总结对比表
| 维度 | 普通插槽 | 作用域插槽 |
|---|
| 数据来源 | 父组件 | 子组件 |
| 存储形式 | VNode数组 | 函数 |
| 编译时机 | 父组件编译时 | 子组件运行时调用 |
| 使用场景 | 布局、内容填充 | 自定义渲染,列表渲染 |
| 灵活性 | 低(内容固定) | 高(可动态渲染) |
| 数据流向 | 父 → 子(仅传递内容) | 子 → 父 (仅传递数据) |
vue3变化,scopedSlots被移除,统一使用slots,所有插槽都是函数。
<template>
<div>
<slot></slot>
<slot name="item" :data="itemData"></slot>
</div>
</template>
<script setup>
import { onMounted } from 'vue'
const itemData = { name: 'Vue 3', version: 3 }
onMounted(() => {
console.log(typeof $slots.default)
console.log(typeof $slots.item)
const defaultVNode = $slots.default()
const itemVNode = $slots.item({ data: itemData })
console.log(Array.isArray(defaultVNode))
})
</script>
| 特性 | Vue 2 | Vue 3 |
|---|
| 普通插槽存储 | $slots (VNode 数组) | $slots (函数) |
| 作用域插槽存储 | $scopedSlots (函数) | $slots (函数) |
| 模板语法 | slot + slot-scope | 统一 v-slot 或 # |
| 访问方式 | this.$slots / this.$scopedSlots | useSlots() 或 $slots |
| 类型判断 | Array.isArray($slots.default) | typeof $slots.default === 'function' |
| 调用方式 | 普通插槽直接使用,作用域插槽需调用 | 所有插槽都需调用 |