vue 中 slot 插槽的使用方法

50 阅读2分钟

我们在做项目的过程中经常会封装一些公用的组件,通过props传值来展示不同的内容,就比如说下面的一个简单的NavBar组件,可以通过传入不同的title展示不同的标题。

NavBar组件

<!-- NavBar -->
<template>
  <div class='navbar'>
    <div class="left">
      <span>&lt;</span>
      <span>返回</span>
    </div>
    <div class="center">
      {{ title }}
    </div>
    <div class="right">
      右侧
    </div>
  </div>
</template>

父组件

<template>
  <div class='home'>
    <NavBar title="标题" />
  </div>
</template>

slot01.png

但是如果需求是标题处展示icon或者icon加文字呢?这个时候就要通过slot插槽来解决了。

一、匿名插槽

匿名插槽只要在NavBar组件需要自定义的地方通过<slot></slot>进行占位,然后在父组件<NavBar></NavBar>标签之间通过<template></template>或者直接写要往插槽中填充的内容。

如果NavBar组件中没有使用slot标签,下面的imgspan标签就不会渲染出来。

NavBar组件

<!-- NavBar -->
<template>
  <div class='navbar'>
    <div class="left">
      <span>&lt;</span>
      <span>返回</span>
    </div>
    <div class="center">
      <slot></slot>
    </div>
    <div class="right">
      右侧
    </div>
  </div>
</template>

父组件

<template>
  <div class='home'>
    <NavBar>
      <img class="icon" src="../../assets/image/search.png" alt="">
      <span>搜索</span>
    </NavBar>
  </div>
</template>

slot02.png

二、具名插槽

有时在一个组件中包含多个插槽出口,比如在我们的NavBar组件中,分为左侧、中间、右侧三个地方,则需要三个slot,此时可以给不同的slot添加name属性作为唯一的id,来确定每一处要渲染的内容,而没有添加name属性的slot会被隐式命名为default

Vue2.6之前的写法为slot="name"Vue2.6之后的写法为v-slot:name,也可以简写为#name

NavBar组件

<template>
  <div class='navbar'>
    <div class="left">
      <slot name="left"></slot>
    </div>
    <div class="center">
      <slot></slot>
    </div>
    <div class="right">
      <slot name="right"></slot>
    </div>
  </div>
</template>

父组件

<template>
  <div class='home'>
    <NavBar>
      <template #left>
        <span>&lt;</span>
        <span>返回</span>
      </template>
      <template #>
        <img class="icon" src="../../assets/image/search.png" alt="">
        <span>搜索</span>
      </template>
      <template #right>
        右侧
      </template>
    </NavBar>
  </div>
</template>

slot03.png

三、插槽默认内容

在父组件没有提供任何内容的情况下,我们可以为插槽指定默认内容,比如说我们的NavBar组件,我希望左侧默认就是返回,中间的标题默认就是传过去的文字,而右侧默认就是空的。

NavBar组件

<template>
  <div class='navbar'>
    <div class="left">
      <slot name="left">
        <span>&lt;</span>
        <span>返回</span>
      </slot>
    </div>
    <div class="center">
      <slot>{{ title }}</slot>
    </div>
    <div class="right">
      <slot name="right"></slot>
    </div>
  </div>
</template>

父组件

<template>
  <div class='home'>
    <NavBar title="标题"></NavBar>
  </div>
</template>

此时我们在父组件中没有提供插槽内容,< 返回和中间传入的title就会作为默认插槽内容展示出来。

slot04.png

三、作用域插槽

在父组件中使用NavBar的时候,插槽内容可以访问到父组件的数据作用域,因为变量本来就是在父组件中定义的,如下:

父组件

<template>
  <div class='home'>
    <NavBar title="标题">
      <template #right>
        {{ rightValue }}
      </template>
    </NavBar>
  </div>
</template>
<script setup lang='ts'>
import { ref } from 'vue';
import NavBar from './NavBar.vue';
const rightValue = ref('右侧')
</script>

slot05.png

但是插槽中无法访问到子组件的状态,此时我们可以在slot标签上添加自定义的字段像父组件通过props给子组件传值一样把数据传递到父组件中。例如:

NavBar组件

<template>
  <div class='navbar'>
    <div class="left">
      <slot name="left">
        <span>&lt;</span>
        <span>返回</span>
      </slot>
    </div>
    <div class="center">
      <slot>{{ title }}</slot>
    </div>
    <div class="right">
      <slot name="right" :city="city"></slot>
    </div>
  </div>
</template>

<script name="NavBar" setup lang='ts'>
import { ref } from 'vue';

defineProps<{
  title: string
}>()

const city = ref('上海')
</script>

父组件

<template>
  <div class='home'>
    <NavBar title="标题">
      <template #right="data">
        {{ data.city }}
      </template>
    </NavBar>
  </div>
</template>

slot06.png

Vue2.6之前的写法为<template slot="name" slot-scope="data">Vue2.6之后的写法为v-slot:name="data",也可以简写为#name="data"

四、动态插槽名

v-slot:也可以使用动态传参,写作v-slot:[name]或简写为#[name]。例如:

父组件

<template>
  <div class='home'>
    <NavBar title="标题">
      <template #[slotName]>
        我是 {{ slotName }}
      </template>
    </NavBar>
  </div>
</template>

<script setup lang='ts'>
import { ref } from 'vue';
import NavBar from './NavBar.vue';
const slotName = ref('right')
</script>

slot07.png