默认插槽

默认插槽是最基础的插槽类型,它允许父组件向子组件传递内容,这些内容会被渲染在子组件的默认插槽位置上。

假设有子组件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属性。