vue3之slot的拓展:
1.通过slot获取slot属性
//父组件
<div>
<AppDashboardFloatingFilters
v-if="dashboardFilters"
v-slot="slotProps"
>
<MyDashboardEventProposalsFilters
v-model:search="searchModel"
:query-params
:status-types="statusItems"
:is-floating="slotProps.isFloating"
@change-status="changeStatus"
@change-search="changeSearch"
/>
</AppDashboardFloatingFilters>
</div>
//子组件 AppDashboardFloatingFilters.vue
<template>
<transition
enter-from-class="-translate-y-[100%]"
leave-to-class="-translate-y-[100%]"
enter-active-class="transition-transform duration-[.5s]"
leave-active-class="transition-transform duration-[.5s]"
>
<div
v-if="!dashboardProposalFiltersVisible && !isMobileViewport()"
class="fixed right-0 top-[4.3rem] z-1 bg-white px-5 shadow-md"
:style="[`left: ${sidebarWidth}`]"
>
<slot :is-floating="true" />
</div>
</transition>
<slot
v-if="dashboardProposalFiltersVisible || isMobileViewport()"
:is-floating="false"
/>
</template>
分析:
上面的 v-slot="slotProps" 作用是,通过 slotProps 获取组件自身定义的 slot 属性(有个前提,必须是可以展示的slot), 如果有多个slot, 可以获取每个slot的共同属性,也可以获取各自特有的属性, 然后拿到这个属性在后面的代码里使用,如 :is-floating="slotProps.isFloating", 这种场景只有在 只有default slot的时候才能这么用,如下补充说明
2.通过slot判断组件定义的slot使用情况
//组件
...
...
//ts
const slots = defineSlots<{
default: () => void;
loader: () => void;
noData: () => void;
footer: () => void;
footerLeft: () => void;
footerRight: () => void;
pagination: () => void;
}>();
const hasAnyFooterSlots = computed(() => {
return (
!!slots.footer ||
!!slots.footerLeft ||
!!slots.footerRight ||
!!slots.pagination
);
});
//页面信息
<slot
v-if="hasAnyFooterSlots"
name="footer"
>
<div
class="mt-11 flex w-full flex-wrap items-center justify-center gap-2 text-sm empty:hidden lg:min-h-16"
>
<div
v-if="$slots.footerLeft"
class="flex flex-wrap items-center justify-center gap-2 lg:mr-auto"
>
<slot name="footerLeft" />
</div>
<div
v-if="$slots.footerRight || $slots.pagination"
class="flex flex-wrap items-center justify-center gap-2 lg:ml-auto"
>
<slot name="footerRight">
<slot
v-if="hasTableData"
name="pagination"
/>
</slot>
</div>
</div>
</slot>
...
...
分析:
上面的 defineSlots 用来定义当前组件内所有的 slot信息,然后可以通过 slots 获取指定的slot, 用来做判断,比如计算属性 hasAnyFooterSlots,以及后面的代码里使用 v-if="$slots.footerLeft" 进行动态判断
订正一下:
上面的 defineSlots 用来定义当前组件内所有的 slot信息,然后可以通过 slots 获取指定的slot, 用来做判断,比如计算属性 hasAnyFooterSlots,以及后面的代码里使用 v-if="$slots.footerLeft" 进行动态判断,注意 v-if="$slots.footerLeft" 中的 $slots 并不是前面定义的 slots, 前面那个课取任何名称。但是后面的 $slots 是 Vue 中的一个对象,包含了所有传递到当前组件的插槽。
关于 $slots 对象:
如果某个插槽被父组件填充了,$slots['插槽名'] 会返回一个内容函数(Vue 渲染函数)。
如果插槽未被填充,$slots['插槽名'] 通常是 undefined
v-if="$slots['left-icon']" 的作用
v-if="$slots['left-icon']" 用来检查:
当前组件是否接收到了一个名为 left-icon 的插槽。
如果接收到了,则渲染对应的模板。
这段代码确保只有当父组件提供了 left-icon 插槽时,组件才会尝试渲染 #left-icon 的 <template> 内容。
两种写法:
v-if="$slots.left-icon" 的写法是完全合法的,并且常用于动态判断插槽是否存在。
使用 $slots['left-icon'] 比 $slots.left-icon 更加通用,因为它适用于所有插槽名称,而不仅限于固定名称。
对于有 ts 类型检查的项目,需要先声明slot,如下:
defineSlots<{
"left-icon"?: () => void;
"right-icon"?: () => void;
}>();
如果没有 ts 类型检查,可以不用声明,完全可以正常运行。
最新测试表明,还需要注意如下问题:
vue3中 v-slot="slotProps" 的用法:
`v-slot`指令是Vue 3中用于接收插槽内容的一种方式。当一个组件传递数据到它的插槽时,我们可以使用`v-slot`来访问这些数据。
这里是一个简单的例子来说明它的用法:
```vue
<!-- 父组件 -->
<template>
<ChildComponent>
<template v-slot="slotProps">
{{ slotProps.item }}
</template>
</ChildComponent>
</template>
<!-- 子组件 (ChildComponent) -->
<template>
<div>
<slot :item="{ name: 'Vue 3', version: '3.0' }"></slot>
</div>
</template>
在这个例子中:
子组件(
ChildComponent)定义了一个插槽,并通过:item属性向插槽传递了一个对象。父组件使用
v-slot="slotProps"来接收这个传递的数据。slotProps是一个包含所有插槽属性的对象。然后,我们可以在父组件中通过
slotProps.item来访问子组件传递的数据。
v-slot指令还有一些其他的用法:
具名插槽:
<template v-slot:header="headerProps"> {{ headerProps.title }} </template>解构插槽 prop:
<template v-slot="{ item }"> {{ item.name }} </template>缩写语法 (#):
<template #header="{ item }"> {{ item.name }} </template>
这就是v-slot="slotProps"的基本使用方法。它允许我们在使用组件时,灵活地接收和使用组件传递给插槽的数据。
还有一种非常特殊的用法:
<!-- 父组件 -->
<template>
<ChildComponent v-slot="slotProps">
//这个时候可以拿到子组件中 slot 属性的值,在调用子组件里面的任何地方使用
{{ slotProps.item }}
// 如果又调用了其它组件,可以把 slotProps.item 传递到 其它组件的props里面
</ChildComponent>
</template>
<!-- 子组件 (ChildComponent) -->
<template>
<div>
<slot :item="{ name: 'Vue 3', version: '3.0' }"></slot>
</div>
</template>
上面这种适用于,子组件只有一个 default solt 的情况,并且这个default slot 没有名称,如果子组件有多个slot, 那么上面的写法就会报错: Codegen node is missing for element/if/for node. Apply appropriate transforms first.
并且 slotProps 可以换成任何有意义的名称,比如上面的 headerProps,这只是一个名称.
注意:
1> 父组件通过 props 向子组件传值(包括子组件中的 slot的参数值)
2> 子组件中的 slot, 可以通过 上面的三种 v-slot 用法给父组件传值,注意:不能直接在 <template> 里给slot 传值,因为只能通过 slot 给父组件传值,这个是单向的. 这个问题困扰了我很久,我发现是我想的简单了。
