指尖上的记忆指尖上的记忆
首页
  • 基础
  • Laravel框架
  • Symfony框架
  • 基础
  • Gin框架
  • 基础
  • Spring框架
  • 命令
  • Nginx
  • Ai
  • Deploy
  • Docker
  • K8s
  • Micro
  • RabbitMQ
  • Mysql
  • PostgreSsql
  • Redis
  • MongoDb
  • Html
  • Js
  • 前端
  • 后端
  • Git
  • 知识扫盲
  • Golang
🌟 gitHub
首页
  • 基础
  • Laravel框架
  • Symfony框架
  • 基础
  • Gin框架
  • 基础
  • Spring框架
  • 命令
  • Nginx
  • Ai
  • Deploy
  • Docker
  • K8s
  • Micro
  • RabbitMQ
  • Mysql
  • PostgreSsql
  • Redis
  • MongoDb
  • Html
  • Js
  • 前端
  • 后端
  • Git
  • 知识扫盲
  • Golang
🌟 gitHub

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>

在这个例子中:

  1. 子组件(ChildComponent)定义了一个插槽,并通过:item属性向插槽传递了一个对象。

  2. 父组件使用v-slot="slotProps"来接收这个传递的数据。slotProps是一个包含所有插槽属性的对象。

  3. 然后,我们可以在父组件中通过slotProps.item来访问子组件传递的数据。

v-slot指令还有一些其他的用法:

  1. 具名插槽:

    <template v-slot:header="headerProps">
      {{ headerProps.title }}
    </template>
    
  2. 解构插槽 prop:

    <template v-slot="{ item }">
      {{ item.name }}
    </template>
    
  3. 缩写语法 (#):

    <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 给父组件传值,这个是单向的. 这个问题困扰了我很久,我发现是我想的简单了。