Vue3插槽
一,概念
插槽(Slots)是一种强大的组件复用机制,用于实现组件的内容分发和复用。插槽是组件的特殊占位符,允许父组件向子组件插入自定义内容,其实就是用父组件的自定义内容替换掉这个特殊占位符。通过插槽,子组件可以定义自己的结构,同时允许父组件动态填充部分内容,实现组件的灵活复用。域插槽的核心价值在于将数据逻辑与视图渲染解耦,使组件更加灵活和可复用。通过子组件暴露数据,父组件自定义渲染,既保持了组件的通用性,又满足了个性化需求。在开发通用组件、列表、表单、状态管理等场景中,作用域插槽是必不可少的工具。
二,作用
- 内容分发 子组件通过 标签定义插槽位置,父组件可以在使用子组件时插入具体内容
- 组件复用 相同的子组件可以通过不同的插槽内容实现多样化展示
- 布局和数据解耦 将通用布局逻辑封装在子组件中,具体内容由父组件提供
三,类型
1. 默认插槽(匿名插槽)
子组件中未命名的标签,父组件插入的内容会默认填充到这里
Child.vue
<template><h2>我是子组件的标题哈</h2><div><slot /></div>
</template>
<script setup></script>
App.vue
<template><h1>我是父组件哈</h1><child><p>我是父组件给插槽的内容</p></child>
</template>
<script setup>
import Child from './Child.vue'
</script>
2. 具名插槽
子组件中通过 name 属性命名的插槽,父组件可以通过 v-slot 或 # 语法指定内容填充位置
Child.vue
<template><h2>我是子组件呀</h2><div><slot name='title'></slot><!-- 具名插槽:title --></div><slot/><!-- 默认插槽 --><h3><slot name='content'></slot></h3><!-- 具名插槽:content -->
</template>
<script setup></script>
App.vue
<template><h1>我是父组件哈</h1><child><template #title><i>我是父组件给插槽的标题</i></template><p>我是默认插槽</p><template #content><i>我是父组件给插槽的内容</i></template></child>
</template>
<script setup>
import Child from './Child.vue'
</script>
3. 作用域插槽
一般插槽都是父组件提供内容,子组件无法传递数据给父组件用。但借助作用域插槽,父组件能够获取子组件的数据,进而依据这些数据对内容进行动态渲染。这种方式打破了传统插槽只能传递静态内容的局限。
场景1:列表组件的自定义渲染
需求:封装一个通用列表组件,允许父组件根据条件控制每一项的展示方式。子组件负责数据遍历和结构,父组件控制显示。
App.vue
<template><h1>我是父组件哈</h1><Child :items="users"><template #default="{ item }"><div class="user-card"><h3>{{ item.name }}</h3><p>年龄:{{ item.age }}</p><p v-if="item.sex === 'M'">性别:{{ item.sex }}</p></div></template></Child>
</template>
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'const users = ref([{ id: 1, name: "张三", age: 25 ,sex:'M'},{ id: 2, name: "李四", age: 30 ,sex:'F'},{ id: 3, name: "王五", age: 22 ,sex:'M'}
])
</script>
Child.vue
<template><ul><li v-for="item in items" :key="item.id"><!-- 通过插槽暴露 item 数据 --><slot :item="item"></slot></li></ul>
</template><script setup>
const props = defineProps({items: {type: Array,required: true}
})
</script>
示例2:表单组件的自定义控件
场景:封装一个表单组件,允许父组件自定义输入控件
可以在父组件中修改input插槽内容,改变表单输入控件
App.vue
<template><h1>我是父组件哈</h1><Child><template #input="{ value, update }"><!-- 使用自定义输入控件 --><input type="text" :value="value" @input="update($event.target.value)" placeholder="请输入内容"></template>
</Child>
</template>
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
</script>
Child.vue
<template><form @submit="handleSubmit"><slot name="input" :value="formValue" :update="updateValue"></slot><button type="submit">提交</button></form>
</template><script setup>
import { ref } from 'vue'
const formValue = ref('')const updateValue = (newValue) => {formValue.value = newValue
}const handleSubmit = () => {console.log('提交值:', formValue.value)
}
</script>
四,作用域插槽与普通插槽的对比
五,与$emit对比
作用域插槽和 emit 都能实现子组件向父组件传递数据,不过它们的应用场景和实现方式存在差异。
1.实现机制
- 作用域插槽:子组件把数据以 v-slot 的形式暴露出来,父组件在使用子组件时,可以按需对这些数据进行处理和展示。
- $emit 自定义事件:子组件借助 defineEmits 定义事件,再通过 emit 触发事件并传递数据,父组件监听该事件就能获取到数据。
2.应用场景
- 作用域插槽:适用于子组件已经获取到数据,但要由父组件来决定如何渲染这些数据的情况,例如列表项的渲染。
- $emit 自定义事件:适用于子组件发生某个事件(像点击、输入等)后,需要通知父组件做出响应的场景。