Code前端首页关于Code前端联系我们

Vue3的slot props到底有啥用?怎么用?常见坑咋避?

terry 2小时前 阅读数 6 #SEO
文章标签 Vue3;slot props

Vue3的slot props到底是什么?

你写Vue组件时,有没有碰到过这种情况:子组件里存着循环数据或内部状态,想让父组件在自定义插槽内容时能用上这些数据?slot props就是子组件给插槽传递数据的机制

举个生活例子:你去咖啡店点定制咖啡(父组件写插槽内容),咖啡店(子组件)得把咖啡原料、温度这些信息(slot props)给你,你才能根据这些信息调整配方,在Vue里,子组件通过<slot>标签把内部数据“绑”上去,父组件写插槽模板时就能拿到这些数据,灵活渲染。

怎么在Vue3组件里定义和使用slot props?

得分成子组件传值父组件接收两步来做。

子组件怎么传?

子组件里用<slot>标签,通过v-bind(或简写)把要传递的数据绑成属性,比如做个列表组件<MyList>,内部循环数组listData,想让父组件自定义每个列表项的结构:

<template>
  <div class="my-list">
    <ul>
      <li v-for="(item, index) in listData" :key="index">
        <!-- 给slot绑定item和index这两个props -->
        <slot :item="item" :index="index"></slot>
      </li>
    </ul>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const listData = ref(['苹果', '香蕉', '橙子'])
</script>

这里<slot>上的itemindex,就是要传给父组件插槽的props。

父组件怎么接收?

父组件用<template>配合v-slot(或简写),后面跟="接收的变量",这个变量里就包含了子组件传的所有slot props,比如上面的<MyList>,父组件用的时候:

<template>
  <MyList>
    <!-- #default 对应默认插槽,也可以省略default -->
    <template #default="slotProps">
      <!-- slotProps里有item和index -->
      <span>{{ slotProps.index }} - {{ slotProps.item }}</span>
    </template>
  </MyList>
</template>

要是觉得slotProps太长,还能解构赋值,更简洁:

<template #default="{ item, index }">
  <span>{{ index }} - {{ item }}</span>
</template>

slot props和普通组件props有什么区别?

很多刚学的同学会把这俩搞混,其实它们的数据流向和应用场景完全不同

数据流向不一样

普通props是父组件→子组件:父组件调用子组件时,通过属性把数据传给子组件,子组件用defineProps接收,比如<Child :msg="parentMsg" />msg是父给子的。

slot props是子组件→父组件的插槽内容:子组件主动把内部数据通过<slot>传给父组件的插槽模板,父组件在插槽里接收这些数据。

应用场景不一样

普通props适合父组件控制子组件的基础配置或静态数据,比如给按钮组件传type(主要、次要)、disabled这些属性,子组件内部根据这些props渲染。

slot props适合子组件让父组件自定义部分结构,同时需要子组件内部的数据支撑,比如列表组件,子组件负责拿数据、做布局,父组件只负责“每个列表项长啥样”,但列表项的内容(如item)得子组件给,这时候slot props就必须了。

实际项目中哪些场景适合用slot props?

说几个常见业务场景,你一看就懂:

通用表格组件

后台管理系统里的表格特别适合,表格组件(子组件)负责请求数据、分页、排序,而每一列的内容(比如用户名、操作按钮)让父组件自定义,这时候子组件循环每一行数据,把row(当前行数据)传给父组件的插槽,父组件用row.username,用row.id做操作按钮的参数。

下拉选择器/弹窗组件

比如做个下拉选择器,选项列表在子组件里(可能从接口拿数据),但每个选项的渲染(比如带图标、带描述)让父组件决定,子组件循环选项数组,把每个option对象通过slot props传给父组件,父组件就能用option.iconoption.label自定义选项样式。

布局型组件

比如页面布局组件<PageLayout>,包含头部、侧边栏、内容区,子组件里头部的标题、侧边栏的菜单数据是内部管理的,父组件要自定义头部右侧的操作按钮(需要标题做联动),这时候子组件给头部插槽传title,父组件拿到title后,就能做“标题+操作按钮”的联动逻辑。

用slot props时容易踩哪些坑,怎么避免?

虽然slot props好用,但新手容易掉坑里,提前避坑能省很多调试时间:

坑1:传了props但父组件没收到

原因可能是子组件slot的属性名和父组件接收时的名字对不上,比如子组件写的是data-item="item",父组件却用{ item }解构,这时候拿到的是undefined,解决方法:检查<slot>上的绑定名和父组件解构的变量名是否一致,保持大小写、拼写完全相同。

坑2:命名插槽没写对name

如果子组件用了命名插槽(比如<slot name="header" :title="title"></slot>),父组件得用#header="props"接收,要是写成#default或者名字拼错,也拿不到数据,所以命名插槽的name要和父组件的#name严格对应。

坑3:slot props是“可选”的,没传也不报错

Vue不会强制父组件必须接收slot props,所以如果父组件没写<template #xxx>,子组件传的props相当于“白传”,如果希望父组件必须自定义这部分,最好在文档里说明,或者子组件加默认内容(<slot>默认内容</slot>)。

多个插槽共存时,slot props怎么管理?

实际开发中,组件可能有多个插槽(比如headerfooterdefault),每个插槽可以独立传不同的props,互不影响。

举个例子,做个卡片组件<Card>,有头部、内容、底部三个插槽:

<template>
  <div class="card">
    <!-- 头部插槽,传title -->
    <slot name="header" :title="cardTitle"></slot>
    <!-- 内容插槽,传list数据 -->
    <slot :list="cardList"></slot>
    <!-- 底部插槽,传total -->
    <slot name="footer" :total="cardList.length"></slot>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const cardTitle = ref('我的卡片')
const cardList = ref([1, 2, 3])
</script>

父组件用的时候,每个插槽单独接收自己的props:

<template>
  <Card>
    <template #header="{ title }">
      <h2>{{ title }} - 自定义头部</h2>
    </template>
    <template #default="{ list }">
      <ul>
        <li v-for="num in list" :key="num">{{ num }}</li>
      </ul>
    </template>
    <template #footer="{ total }">
      <p>共{{ total }}条数据</p>
    </template>
  </Card>
</template>

每个插槽的props是独立的,父组件按需接收就行,逻辑很清晰。

能不能结合动态插槽和slot props一起用?

当然可以!动态插槽是指用变量来决定渲染哪个插槽(<template #[dynamicSlot]>),结合slot props后,能实现更灵活的逻辑。

比如做个多状态组件,根据status显示不同插槽,同时每个插槽需要对应的数据:

子组件<MultiStatus>

<template>
  <div class="multi-status">
    <slot 
      :name="currentSlot" 
      :data="slotData"
    ></slot>
  </div>
</template>
<script setup>
import { ref, computed } from 'vue'
const status = ref('loading')
const slotData = ref({ loadingText: '加载中...' })
const currentSlot = computed(() => {
  return status.value // 可能是loading、success、error等
})
</script>

父组件根据currentSlot动态渲染,并接收slot props:

<template>
  <MultiStatus>
    <template #[currentSlot]="{ data }">
      <div v-if="currentSlot === 'loading'">
        {{ data.loadingText }}
      </div>
      <div v-else-if="currentSlot === 'success'">
        成功:{{ data.result }}
      </div>
      <!-- 其他状态... -->
    </template>
  </MultiStatus>
</template>
<script setup>
import { ref, watch } from 'vue'
const currentSlot = ref('loading')
// 模拟状态变化
watch(currentSlot, (newVal) => {
  // 这里可以联动子组件的status变化,省略复杂逻辑...
})
</script>

这样既实现了动态切换插槽,又能拿到子组件传给当前插槽的data,灵活性拉满。

slot props的核心价值

Vue3的slot props本质是让子组件把内部数据“开放”给父组件的插槽内容,从而在“子组件封装通用逻辑/结构 + 父组件自定义局部细节”的场景下,实现更松耦合、更灵活的组件设计。

不管是后台系统的表格、前端的弹窗组件,还是复杂布局,只要涉及“子组件有数据,父组件要自定义这部分的UI但需要子组件的数据”,slot props都是绕不开的利器,把它吃透,组件封装能力能上一个大台阶~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门