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>