Vue slot 插槽的使用

Vue 插槽的三种类型及其使用场景

问题

Vue 中的 slot 是什么?有哪些使用场景?

解答

slot 是什么

slot 插槽是组件模板中的占位符,用于承载分发内容。当使用组件时,组件标签内的内容会自动填充到 slot 位置。

可以类比为插卡式游戏机:组件暴露插槽,用户插入自定义内容。

使用场景

插槽让组件更灵活,可以在不同场景下复用组件并做定制化处理。常见场景包括:

  • 布局组件
  • 表格列定制
  • 下拉选项
  • 弹框内容

默认插槽

子组件用 <slot> 标签确定渲染位置,标签内可放默认内容。

子组件 Child.vue:

<template>
  <slot>
    <p>插槽后备的内容</p>
  </slot>
</template>

父组件:

<Child>
  <div>默认插槽</div>  
</Child>

具名插槽

子组件用 name 属性标识插槽,父组件用 v-slot 指定插槽名。

子组件 Child.vue:

<template>
  <slot>插槽后备的内容</slot>
  <slot name="content">插槽后备的内容</slot>
</template>

父组件:

<child>
  <template v-slot:default>具名插槽</template>
  <template v-slot:content>内容...</template>
</child>

作用域插槽

子组件通过绑定属性将数据传给父组件,父组件通过 v-slot 接收。

子组件 Child.vue:

<template> 
  <slot name="footer" testProps="子组件的值">
    <h3>没传footer插槽</h3>
  </slot>
</template>

父组件:

<child> 
  <template v-slot:default="slotProps">
    来自子组件数据:{{slotProps.testProps}}
  </template>
  <!-- 简写 -->
  <template #default="slotProps">
    来自子组件数据:{{slotProps.testProps}}
  </template>
</child>

语法说明

  • v-slot 只能用在 <template> 上,但只有默认插槽时可以直接用在组件标签上
  • 默认插槽名为 default,可省略
  • 缩写为 # 时不能省略参数,需写成 #default
  • 支持解构:v-slot="{user}",重命名:v-slot="{user: newName}",默认值:v-slot="{user = '默认值'}"

实现原理

slot 本质是返回 VNode 的函数。组件渲染过程:template → render function → VNode → DOM。

定义组件:

Vue.component('button-counter', {
  template: '<div><slot>我是默认内容</slot></div>'
})

使用组件:

new Vue({
  el: '#app',
  template: '<button-counter><span>我是slot传入内容</span></button-counter>',
  components: {buttonCounter}
})

生成的渲染函数:

(function anonymous() {
  with(this){
    return _c('div',[_t("default",[_v("我是默认内容")])],2)
  }
})

其中 _trenderSlot 函数,用于渲染插槽。renderSlot 接收插槽名、默认内容、props 等参数,通过 this.$scopedSlots 获取父组件传入的内容。

作用域插槽能传递数据是因为 renderSlot 执行时会传入 props,父组件通过 v-slot 接收这些数据。

关键点

  • slot 是组件内容分发机制,分为默认插槽、具名插槽和作用域插槽三种
  • 默认插槽直接在组件标签内写内容,具名插槽用 v-slot:name 指定,作用域插槽可接收子组件数据
  • v-slot 可简写为 #,支持解构和默认值语法
  • slot 本质是返回 VNode 的函数,通过 $slots$scopedSlots 实现内容传递