
vue3——插槽
默认插槽
默认插槽是最基础的插槽类型,它允许父组件向子组件传递内容,这些内容会被渲染在子组件的默认插槽位置上。
假设有子组件ChildComponent.vue,代码如下。
<!-- ChildComponent.vue -->
<template>
<div class="child-component">
<slot></slot> <!-- 默认插槽 -->
</div>
</template>
父组件在使用子组件ChildComponent时,可以向其默认插槽中传递内容,代码如下。
<!-- 父组件 -->
<template>
<ChildComponent>
<p>这是一些默认插槽的内容。</p>
</ChildComponent>
</template>
父组件提供的段落将在ChildComponent的<slot></slot>处进行渲染,最终的HTML渲染结果是父组件向子组件传递的内容在子组件的相应位置被渲染。具体来说,渲染结果会是这样的:
<div class="child-component">
<p>这是一些默认插槽的内容。</p>
</div>
代码解释如下。
<div class="child-component">是ChildComponent的根元素。
<p>这是一些默认插槽的内容。</p>是父组件提供给子组件的插槽内容。
可以看到,通过插槽机制,父组件能够影响子组件内部的内容布局。
具名插槽
具名插槽在Vue中提供了一种定义多个不同内容区域的方式,每个插槽都有一个独特的名字,非常适用于创建复杂的布局。
以下代码在ChildComponent.vue中定义了三个插槽:一个默认插槽和两个具名插槽(header和footer)。
<!-- ChildComponent.vue -->
<template>
<div class="child-component">
<header>
<slot name="header"></slot> <!-- 具名插槽:header -->
</header>
<main>
<slot></slot> <!-- 默认插槽 -->
</main>
<footer>
<slot name="footer"></slot> <!-- 具名插槽:footer -->
</footer>
</div>
</template>
父组件通过v-slot:header和v-slot:footer向子组件的具名插槽传递内容。
<!-- 父组件 -->
<template>
<ChildComponent>
<template v-slot:header>
<h1>页头内容</h1>
</template>
<p>主要内容</p>
<template v-slot:footer>
<p>页脚内容</p>
</template>
</ChildComponent>
</template>
父组件中的<template>标签用于定义组件的结构或模板。它不是一个真实的DOM元素,而是一个用于包裹实际要渲染的HTML结构的容器。在Vue的渲染过程中,<template>标签本身不会被渲染到最终的HTML中,只有其内部的内容会被渲染,这个标签在实现插槽时尤为重要。
在以上示例中最终的HTML渲染结果如下。
<div class="child-component">
<header>
<h1>页头内容</h1> <!-- 来自父组件的header插槽内容 -->
</header>
<main>
<p>主要内容</p> <!-- 来自父组件的默认插槽内容 -->
</main>
<footer>
<p>页脚内容</p> <!-- 来自父组件的footer插槽内容 -->
</footer>
</div>
在这个结果中,<div class="child-component">是ChildComponent的根元素。<header>、<main>和<footer>分别是子组件中定义的部分,它们包裹着父组件提供给对应插槽的内容。
header元素中显示的是v-slot:header中定义的<h1>页头内容</h1>。
main元素中显示的是默认插槽中定义的<p>主要内容</p>。
footer元素中显示的是v-slot:footer中定义的<p>页脚内容</p>。
作用域插槽
作用域插槽是Vue中一种高级的插槽类型,它允许子组件将数据传递回父组件,以便父组件可以使用这些数据来渲染插槽内容。这种类型的插槽在创建动态列表或数据驱动的组件时特别有用。
例如,在ChildComponent.vue中定义了一个作用域插槽,遍历items数组,并将每个item对象作为插槽的属性传递,代码如下。
<!-- ChildComponent.vue -->
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item"></slot> <!-- 作用域插槽 -->
</li>
</ul>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
items: Array
});
</script>
父组件通过<ChildComponent>传递itemsList作为items属性,并通过作用域插槽接收子组件传递的每个item,代码如下。
<!-- 父组件 -->
<template>
<ChildComponent :items="itemsList">
<template v-slot="{ item }">
<span>{{ item.name }}</span>
</template>
</ChildComponent>
</template>
<script setup>
import { ref } from 'vue';
const itemsList = ref([{ id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }]);
</script>
最终的HTML渲染结果如下。
<ul>
<li>
<span>Item 1</span> <!-- 来自 itemsList 第一个元素的 name 属性 -->
</li>
<li>
<span>Item 2</span> <!-- 来自 itemsList 第二个元素的 name 属性 -->
</li>
</ul>
在这个结果中,每个<li>中的元素都包含一个<span>,显示了父组件中itemsList数组里对应item的name属性。