Skip to content

Vue 插槽

在某些场景中,我们可能需要从父组件向子组件传递模板内容。例如,一个通用的卡片组件,我们希望它的主体内容是可定制的。这时,就需要使用 插槽 (Slots)

插槽是 Vue 组件的一个强大功能,它允许你以一种预定义的方式组合组件,将父组件的内容“插入”到子组件的指定位置。

默认插槽 (Default Slot)

最简单的插槽形式是单个的、默认的插槽。子组件可以使用 <slot> 标签来为其自身开辟一个“内容出口”。

FancyButton.vue (子组件)

vue
<template>
  <button class="fancy-btn">
    <slot></slot> <!-- 插槽出口 -->
  </button>
</template>

<style>
.fancy-btn {
  background: linear-gradient(315deg, #42d392 25%, #647eff);
  border: none;
  padding: 8px 16px;
  border-radius: 8px;
  color: white;
  cursor: pointer;
}
</style>

父组件在使用 FancyButton 时,可以向其内部传递任何模板内容。这些内容将被渲染在子组件的 <slot> 标签所在的位置。

App.vue (父组件)

vue
<script setup>
import FancyButton from './FancyButton.vue'
</script>

<template>
  <FancyButton>
    Click me! <!-- 插槽内容 -->
  </FancyButton>
</template>

渲染出的 HTML 将是:

html
<button class="fancy-btn">
  Click me!
</button>

具名插槽 (Named Slots)

有时我们需要多个插槽。例如,一个带有页头、主体和页脚的布局组件。为此,<slot> 元素有一个特殊的 attribute name,可以用来给不同的插槽分配唯一的 ID。

BaseLayout.vue (子组件)

vue
<template>
  <div class="container">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot> <!-- 默认插槽 -->
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

要在父组件中向具名插槽提供内容,我们需要使用一个带有 v-slot 指令的 <template> 元素,并将插槽名作为 v-slot 的参数。

App.vue (父组件)

vue
<script setup>
import BaseLayout from './BaseLayout.vue'
</script>

<template>
  <BaseLayout>
    <template v-slot:header>
      <h1>Here might be a page title</h1>
    </template>

    <template v-slot:default>
      <p>A paragraph for the main content.</p>
      <p>And another one.</p>
    </template>

    <template v-slot:footer>
      <p>Here's some contact info</p>
    </template>
  </BaseLayout>
</template>

v-slot 有一个专门的简写语法 #,所以 <template v-slot:header> 可以被简写为 <template #header>

作用域插槽 (Scoped Slots)

有时,让插槽内容能够访问子组件中才有的数据是很有用的。例如,一个列表组件,我们希望父组件能自定义每一项的渲染方式。

为了实现这一点,我们可以像对 prop 那样,给插槽传递 attribute。

MyComponent.vue (子组件)

vue
<script setup>
import { ref } from 'vue'
const items = ref(['Feed a cat', 'Buy milk'])
</script>

<template>
  <ul>
    <li v-for="(item, index) in items">
      <slot :item-text="item" :index="index"></slot>
    </li>
  </ul>
</template>

现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字。

App.vue (父组件)

vue
<script setup>
import MyComponent from './MyComponent.vue'
</script>

<template>
  <MyComponent v-slot="slotProps">
    <i>{{ slotProps.itemText }} (index: {{ slotProps.index }})</i>
  </MyComponent>
</template>

在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。你也可以使用解构赋值来获取特定的 prop:

vue
<MyComponent v-slot="{ itemText, index }">
  <i>{{ itemText }} (index: {{ index }})</i>
</MyComponent>

作用域插槽使得我们可以创建出高度灵活和可复用的组件,将数据逻辑封装在子组件中,同时将视图表现的控制权交给父组件。

本站内容仅供学习和研究使用。