Vue 插槽

在相应的组件 A 中插入的内容会替换掉 A 中的 slot 元素

1. 具名插槽

通过在 slot 元素上添加 name="xxx" 字段, 即可将相应的 slot 插槽命名为 xxx
当该组件被父组件调用时, 父组件可以通过 <template v-slot:xxx>...</template> 或者 <template #xxx>...</template> 对具名插槽进行替换

1.1 实现效果

在子组件 nav-bar 中, 有 left, center, right 三个具名插槽
而在父组件 app 中调用 nav-bar 子组件时, 通过插入 <template v-slot:slotName>replace slot contents</template> 来对具名插槽进行替换. 具体效果如下:

  • left 插槽被替换为 button 按钮
  • center 插槽被替换为 a 标签
  • right 插槽被替换为 input 输入框

1.2 具体代码

1.2.1 nav-bar 组件

<template>
  <div class="nav-bar">
    <div class="left">
      <slot name="left">left</slot>
    </div>
    <div class="center">
      <slot name="center">center</slot>
    </div>
    <div class="right">
      <slot name="right">right</slot>
    </div>
  </div>
</template>

1.2.2 App 组件

<nav-bar>
  <template v-slot:left>
    <button>left content</button>
  </template>
  <template #center> <!-- # 是 v-slot: 的语法糖 -->
    <a href="#">center content</a>
  </template>
  <template #right>
    <input type="text" placeholder="right content">
  </template>
</nav-bar>

没有起名字的插槽会默认 name="default"

2. 动态插槽

动态插槽最关键的地方是为 template 元素添加一个动态绑定插槽的名字(通过动态指令参数--即将参数用 [] 包裹起来, 即可实现), 语法为

<!-- 需要在 script 代码块中创建 dynamicSlotNames 变量 -->
<template v-slot:[dynamicSlotNames]>
  <!-- 用于插入插槽的内容 -->
</template>

2.1 实现效果

在没有对插槽进行替换时, 每个插槽都有自己的默认内容(left, center, right)

而通过点击 left, center, right 按钮, 分别会将 slotName 变量的值修改为 left, center, right, 从而匹配 nav-bar 组件中的插槽的 name, 进而对插槽进行替换
而被替换的插槽的内容也会变为 button this is slot content, 如下图所示:

2.2 具体实现

2.2.1 nav-bar 组件

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

const leftContent = ref('left')
const centerContent = ref('center')
const rightContent = ref('right')
</script>

<template>
  <div class="nav-bar">
    <div class="left">
      <slot name="left">
        {{ leftContent }}
      </slot>
    </div>
    <div class="center">
      <slot name="center">
        {{ centerContent }}
      </slot>
    </div>
    <div class="right">
      <slot name="right">
        {{ rightContent }}
      </slot>
    </div>
  </div>
</template>

2.2.2 App 组件

<script setup lang="ts">
import NavBar from './views/NavBar.vue'
import { ref } from 'vue'

const slotName = ref('')
const handleClick = (e: Event) => {
  const elem = e.target as HTMLElement
  if (elem.matches('button')) slotName.value = elem.innerHTML
}
</script>

<template>
  <div class="App">
    <h2>App</h2>
    <nav-bar>
      <template #[slotName]> button this is slot content </template>
    </nav-bar>
    <div @click="handleClick">
      <button>left</button>
      <button>center</button>
      <button>right</button>
    </div>
  </div>
</template>

3. 作用域插槽

当我们同时需要子组件的数据父组件中用于替换插槽内容的组件时, 我们会用到作用域插槽

<!-- 子组件中 -->
<slot name="slotName" :propName1="key" :propName2="value"></slot>

<!-- 父组件中 -->
<children-component>
  <!-- slotProps 是子组件的插槽向父组件传递的值的集合 -->
  <!-- 而且只能用在相应的有 v-slot 或者 # 字段的元素中, 在此之外是用不了的 -->
  <!-- 通过 slotProps.propName1 即可获得 slotName 插槽所传递的 key -->
  <!-- 通过 slotProps.propName2 即可获得 slotName 插槽所传递的 value -->
  <template v-slot:slotName="slotProps">
    key: {{ slotProps.propName1 }}, value: {{ slotProps.propName2 }}
  </template>
</children-component>

3.1 实现效果

在没有对插槽进行替换时, 每个插槽都有自己的默认内容

对于给出的三个可以控制插槽内容的按钮, 点击某一个按钮, 对应的插槽内容也会变为按钮. 而按钮中的内容则由子组件的数据给出

3.2 具体实现

3.2.1 nav-bar 组件

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

const leftContent = ref('left slot content')
const centerContent = ref('center slot content')
const rightContent = ref('right slot content')
</script>

<template>
  <div class="nav-bar">
    <div class="left">
      <slot name="left" :content="leftContent">
        {{ leftContent }}
      </slot>
    </div>
    <div class="center">
      <slot name="center" :content="centerContent">
        {{ centerContent }}
      </slot>
    </div>
    <div class="right">
      <slot name="right" :content="rightContent">
        {{ rightContent }}
      </slot>
    </div>
  </div>
</template>

3.2.2 App 组件

<script setup lang="ts">
import NavBar from './views/NavBar.vue'
import { ref } from 'vue'

const slotName = ref('left')
const handleClick = (e: Event) => {
  const elem = e.target as HTMLElement
  if (elem.matches('button')) slotName.value = elem.innerHTML
}
</script>

<template>
  <div class="App">
    <h2>App</h2>
    <nav-bar>
      <!-- 如果使用 # 语法糖, 则 ts 有时可能会报错 -->
      <template v-slot:[slotName]="props">
        <button>{{ props.content }}</button>
      </template>
    </nav-bar>
    <div @click="handleClick">
      <button>left</button>
      <button>center</button>
      <button>right</button>
    </div>
  </div>
</template>
posted @ 2023-04-03 20:57  小阁下  阅读(43)  评论(0编辑  收藏  举报