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

Vue3里slot嵌套该怎么用?从基础到进阶全搞懂

terry 4小时前 阅读数 13 #SEO
文章标签 Vue3;slot嵌套

slot嵌套的核心逻辑是啥?

你可以把 slot 理解成“组件里的占位符”——父组件往这个占位符里塞内容,子组件负责把内容显示在指定位置,那 slot嵌套 呢?占位符里再放占位符”,比如父组件给子组件A传内容,子组件A的某个区域又要用子组件B来渲染,而子组件B也需要父组件来控制内容,这时候子组件A的 slot 里就得再放子组件B的 slot,形成嵌套关系。

简单说,slot嵌套是为了让父组件能“穿透”多层子组件,精准控制最深层的自定义内容,比如做一个带头部、内容、底部的弹窗组件,头部里还要放自定义的标题和操作按钮,这时候头部组件(子组件)的 slot 里得再放按钮的 slot(孙子级 slot),父组件既能控制弹窗整体结构,又能控制头部里的按钮。

基础玩法:父→子的单层级slot嵌套咋写?

先看最基础的“父组件→子组件→孙子组件”嵌套结构,用代码直观理解:

子组件(Child.vue)

<template>
  <div class="child-box">
    <h2>我是子组件</h2>
    <!-- 这里的<slot>是父组件的“入口”,但slot里又用了GrandChild组件 -->
    <slot>
      <!-- 如果父组件没传内容,就显示fallback内容(GrandChild) -->
      <GrandChild>
        <!-- GrandChild里的<slot name="grand-slot">是孙子级占位符 -->
        <slot name="grand-slot"></slot>
      </GrandChild>
    </slot>
  </div>
</template>
<script setup>
import GrandChild from './GrandChild.vue'
</script>

孙子组件(GrandChild.vue)

<template>
  <div class="grand-box">
    <h3>我是孙子组件</h3>
    <!-- 这里的<slot>是给父组件控制内容的入口 -->
    <slot></slot>
  </div>
</template>

父组件(Parent.vue)

<template>
  <Child>
    <!-- 填充子组件的默认slot -->
    <template #default>
      <p>父组件给子组件的内容</p>
      <!-- 填充孙子组件的grand-slot -->
      <template #grand-slot>
        <button>父组件控制的孙子组件按钮</button>
      </template>
    </template>
  </Child>
</template>

逻辑拆解

  • 父组件用 <Child> 时,通过 #default 给子组件传内容;
  • 子组件的 <slot> 里嵌套了 <GrandChild>,而 <GrandChild> 里又有自己的 <slot name="grand-slot">
  • 父组件要控制孙子组件的内容,就得在 #default 里再用 #grand-slot 来填充——这就是 单层级嵌套(父→子→孙)的核心逻辑。

进阶:多层嵌套(父→子→孙→曾孙)咋玩数据传递?

当嵌套层级变多(父→子→孙→曾孙”),数据怎么从最外层传到最内层?这时候 作用域插槽(带数据的 slot)必须安排上。

举个“用户信息多层传递”的例子:

  • 子组件有用户数据 user,要传给孙子组件;
  • 孙子组件再把数据传给曾孙组件;
  • 父组件不仅要控制曾孙组件的内容,还要拿到最外层的用户数据。

子组件(Child.vue)

<template>
  <div class="child">
    <h3>子组件</h3>
    <!-- 作用域插槽:把user数据传给父组件 -->
    <slot :user="user"></slot>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const user = ref({ name: '小明', age: 18 })
</script>

孙子组件(GrandChild.vue)

<template>
  <div class="grand">
    <h4>孙子组件</h4>
    <!-- 把收到的user再传给曾孙(通过v-bind透传) -->
    <slot v-bind="props.user"></slot>
  </div>
</template>
<script setup>
const props = defineProps(['user'])
</script>

曾孙组件(GreatGrand.vue)

<template>
  <div class="great-grand">
    <h5>曾孙组件</h5>
    <!-- 显示用户数据 -->
    <p>姓名:{{ user.name }}, 年龄:{{ user.age }}</p>
    <!-- 曾孙的slot,父组件可以控制这里的内容 -->
    <slot></slot>
  </div>
</template>
<script setup>
defineProps(['user'])
</script>

父组件(Parent.vue)

<template>
  <Child v-slot="childScope"> 
    <!-- childScope里有子组件传的user -->
    <GrandChild v-slot="grandScope"> 
      <!-- grandScope里有孙子组件传的user(和childScope.user是同一个) -->
      <GreatGrand :user="grandScope.user"> 
        <button>父组件控制的曾孙按钮</button>
      </GreatGrand>
    </GrandChild>
  </Child>
</template>

关键点

  • 每一层组件都用 作用域插槽<slot :数据="变量">)把数据传出去;
  • 父组件用 v-slot="作用域变量" 接收上一层的数据,再传给下一层;
  • 这样不管嵌套多少层,数据都能从外层“穿透”到内层,父组件也能精准控制每一层的内容。

作用域插槽和slot嵌套结合时,怎么避免作用域混乱?

多层嵌套+作用域插槽最容易踩的坑是 “作用域变量重名”——比如子组件和孙子组件都传了个 user,父组件接收时分不清谁是谁。

解决方法很简单:给作用域变量“重命名”,用 ES6 的结构赋值就行。

看优化后的父组件代码:

<template>
  <Child v-slot="{ user: childUser }"> 
    <!-- 把childScope的user重命名为childUser -->
    <GrandChild v-slot="{ user: grandUser }"> 
      <!-- 把grandScope的user重命名为grandUser -->
      <GreatGrand :user="grandUser"> 
        <p>父组件里用子组件数据:{{ childUser.name }}</p>
        <button>按钮</button>
      </GreatGrand>
    </GrandChild>
  </Child>
</template>

这样一来,childUser 明确是子组件传的,grandUser 是孙子组件传的,再也不会搞混啦~

实际项目中,哪些场景必须用slot嵌套?

很多“需要多层自定义”的组件场景,都得靠 slot 嵌套实现,举几个真实开发中常见的例子:

组件库的“布局组件”

AntDesignVue 的 Layout 组件,它包含 HeaderSiderContentFooter 这几个子组件,每个子组件内部又允许用户自定义内容(Header 里放 logo 和导航按钮),这时候:

  • Layout<slot> 里嵌套 Header 组件;
  • Header 组件的 <slot> 里再嵌套“logo 插槽”和“导航插槽”;
  • 父组件通过多层 v-slot,就能同时控制布局结构和每个区域的细节。

表格组件的“自定义列”

像 ElementPlus 的 Table 组件,TableColumn 可以自定义单元格内容(比如渲染按钮、图片),这时候:

  • Table<slot> 里嵌套 TableColumn
  • TableColumn<slot> 里再嵌套“单元格内容插槽”;
  • 父组件用 #default 控制列结构,用 #cell 控制单元格里的自定义内容,实现“一列一逻辑”。

弹窗组件的“多层自定义”

做一个带头部、内容、底部的 Modal 弹窗:

  • Modal<slot> 里嵌套 ModalHeaderModalContentModalFooter
  • ModalHeader<slot> 里再嵌套“标题插槽”和“关闭按钮插槽”;
  • 父组件既能控制弹窗整体显示(比如是否显示头部),又能控制头部里的标题和按钮,灵活性拉满。

slot嵌套时,动态插槽名怎么玩?

动态插槽名是指“slot 的名称由变量决定”,语法是 v-slot:[动态变量],在嵌套场景中,它能让你根据业务逻辑 动态决定渲染哪个 slot

举个“根据用户权限显示不同内容”的例子:

子组件(Child.vue)

<template>
  <div>
    <slot name="admin"></slot> <!-- 管理员专属slot -->
    <GrandChild>
      <!-- 动态传slot名给孙子组件 -->
      <slot :dynamicSlot="currentSlot"></slot>
    </GrandChild>
    <slot name="guest"></slot> <!-- 游客专属slot -->
  </div>
</template>
<script setup>
import { ref } from 'vue'
const currentSlot = ref('user-content') // 根据权限动态变化
</script>

孙子组件(GrandChild.vue)

<template>
  <div>
    <!-- 接收动态slot名,渲染对应的内容 -->
    <slot :name="props.dynamicSlot"></slot>
  </div>
</template>
<script setup>
defineProps(['dynamicSlot'])
</script>

父组件(Parent.vue)

<template>
  <Child>
    <template #admin>
      <p>管理员才能看的内容</p>
    </template>
    <!-- 动态绑定slot名,currentSlot变了,渲染的内容也变 -->
    <template v-slot:[currentSlot]="scope">
      <p>这里是{{ scope.dynamicSlot }}的内容</p>
    </template>
    <template #guest>
      <p>游客请登录</p>
    </template>
  </Child>
</template>
<script setup>
import { ref } from 'vue'
const currentSlot = ref('user-content')
</script>

核心逻辑

  • 子组件用 dynamicSlot 把“该渲染哪个 slot”的变量传给孙子组件;
  • 孙子组件用 name="props.dynamicSlot" 动态渲染 slot;
  • 父组件用 v-slot:[currentSlot] 动态填充内容;
  • 这样整个嵌套结构的内容渲染逻辑,能根据业务变量(比如用户权限、页面状态)灵活变化。

常见坑点:slot嵌套时数据找不到、slot不渲染咋解决?

新手玩 slot 嵌套,很容易遇到“数据丢了”“slot 没渲染”的问题,总结几个高频坑和解决方案:

坑:“数据作用域错误”,内层slot拿不到外层数据

比如孙子组件想显示子组件的 user,但没通过作用域插槽传递。
解决:确保每一层组件都用 v-bind 把数据传到下一层,父组件用 v-slot 接收数据后再传给内层组件。

坑:“插槽名拼写错误”,导致slot不渲染

比如子组件的 slot 叫 #header,父组件写成 #head,名字对不上就不渲染。
解决:检查所有 name 属性和 v-slot 的名称是否完全一致(包括大小写)。

坑:“fallback内容干扰”,父组件没传内容时,fallback里的嵌套slot自动生效

比如子组件的 <slot> 里有 fallback 内容(默认显示孙子组件),但父组件其实想自定义这部分。
解决:明确 fallback 的作用——它是“父组件没传内容时的兜底”,如果要完全自定义,父组件必须传对应 slot 的内容,覆盖 fallback。

坑:“动态插槽名绑定错误”,写成静态名导致不生效

比如想动态渲染 slot,却写成 <template #currentSlot>(静态名),而不是 <template v-slot:[currentSlot]>
解决:动态插槽名必须用 v-slot:[变量] 的语法,不能用 #变量(后者是静态名)。

slot嵌套的核心价值是“多层自定义”

Vue3 的 slot 嵌套,本质是 让父组件能“穿透”N层子组件,精准控制每一层的自定义内容,无论是做组件库、业务组件还是复杂页面,只要涉及“父组件需要控制多层子组件的局部内容”,slot 嵌套+作用域插槽就是最优解。

记住这几个关键逻辑:

  • 嵌套结构:父→子→孙→…,每一层用 <slot> 做占位符;
  • 数据传递:用 作用域插槽数据="变量")把数据从外层传到内层; 控制:父组件用 v-slot 接收数据,再通过命名 slot(#name)或动态 slot(v-slot:[变量]
  • 避坑技巧:重命名作用域变量、检查插槽名拼写、理解 fallback 逻辑。

把这些逻辑吃透,再复杂的多层嵌套场景也能轻松hold住~

版权声明

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

热门