vue3中的slot使用

226 阅读3分钟

vue3插槽详解

在vue3中,插槽(Slot)的核心概念是为子组件预留内容分发的占位符,让父组件可以传入自定义的模板片段。

 各类插槽核心使用场景

插槽类型核心使用场景关键区别/特点
默认插槽组件内容分发,子组件内单一的不确定内容的区域。父组件提供的内容会替换子组件的 <slot> 标签;子组件可在<slot>中提供默认内容
具名插槽组件内有多个内容分发区域,需要区分使用 name 属性命名插槽(未命名则默认为 "default");父组件使用 v-slot:name 或 #name 指令指定内容
作用域插槽父组件需要使用子组件内部数据来渲染插槽内容子组件通过 <slot> 标签绑定属性(如 :item="item")向父组件传递数据;父组件通过 v-slot:name="slotProps" 接收数据
动态插槽插槽名需要动态变化父组件使用 动态指令参数v-slot:[dynamicSlotName] 或 #[dynamicSlotName]

以下是一个综合了上述各类插槽的示例,包含一个复杂的 ArticleLayout 组件和一个使用它的父组件。

1. 子组件:ArticleLayout.vue

这个组件展示了多种插槽的混合使用。

<template>
  <article class="article-layout">
    <!-- 动态插槽:根据条件动态决定头部插槽 -->
    <header>
      <slot :name="headerSlotName" :title="articleTitle"></slot>
    </header>

    <!-- 默认插槽:用于文章主体内容 -->
    <main>
      <slot>这是默认的主内容,父组件没传内容时显示。</slot>
    </main>

    <!-- 具名插槽:用于作者信息 -->
    <footer>
      <slot name="footer"></slot>
    </footer>

    <!-- 作用域插槽:渲染相关文章列表,父组件决定如何渲染 -->
    <aside>
      <h3>相关推荐</h3>
      <ul>
        <li v-for="related in relatedList" :key="related.id">
          <!-- 子组件将 related 对象作为 prop 传递给父组件 -->
          <slot name="related" :related="related"></slot>
        </li>
      </ul>
    </aside>
  </article>
</template>

<script setup>
import { ref, computed } from 'vue'

// 定义组件接收的属性
const props = defineProps({
  articleTitle: String,
  relatedList: {
    type: Array,
    default: () => []
  }
})

// 动态计算头部插槽名称
const headerSlotName = computed(() => {
  return props.articleTitle ? 'header' : 'default-header'
})
</script>
2. 父组件:ParentComponent.vue

这个组件展示了如何向子组件的各个插槽填充内容,特别是如何使用作用域插槽接收数据。

vue

<template>
  <ArticleLayout 
    :article-title="articleData.title" 
    :related-list="relatedArticles"
  >
    <!-- 动态插槽内容:根据子组件计算的 headerSlotName 填充 -->
    <!-- 同时接收子组件传递的 title 数据 -->
    <template #[headerSlotName]="{ title }">
      <h1>{{ title || '默认标题' }}</h1>
      <p class="publish-time">发布时间: {{ currentTime }}</p>
    </template>

    <!-- 覆盖默认插槽 -->
    <p>{{ articleData.content }}</p>
    <img :src="articleData.imageUrl" alt="文章配图">

    <!-- 具名插槽:填充页脚 -->
    <template #footer>
      <div class="article-footer">
        <span>作者: {{ authorName }}</span>
        <button @click="likeArticle">点赞</button>
      </div>
    </template>

    <!-- 作用域插槽:接收子组件传递的 related 数据,自定义渲染 -->
    <template #related="{ related }">
      <!-- 父组件可以完全控制如何渲染每一项 -->
      <div class="related-item">
        <h4>{{ related.title }}</h4>
        <p>{{ related.summary }}</p>
        <span class="read-count">阅读量: {{ related.views }}</span>
      </div>
    </template>

  </ArticleLayout>
</template>

<script setup>
import { ref, computed } from 'vue'
import ArticleLayout from './ArticleLayout.vue'

// 假设的数据
const articleData = ref({
  title: 'Vue 3 插槽详解',
  content: '这是一篇关于Vue 3插槽使用的详细教程...',
  imageUrl: '/path/to/image.jpg'
})

const relatedArticles = ref([
  { id: 1, title: 'Vue 3 组合式API', summary: '学习组合式API...', views: 1200 },
  { id: 2, title: 'Vue 3 响应式原理', summary: '深入理解响应式...', views: 980 }
])

const authorName = ref('技术博主')

const currentTime = computed(() => new Date().toLocaleDateString())

// 动态插槽名
const headerSlotName = ref('header')

const likeArticle = () => {
  console.log('文章已点赞')
  // 点赞逻辑
}
</script>

重点变化总结

特性Vue 2Vue 3
语法slot-scopev-slot
简写#
具名插槽+作用域slot="name" slot-scope="props"#name="props"
默认插槽作用域slot-scope="props"#default="props"
解构支持