在进行Vue
组件开发时,当组件内某一部分标签不确定,或者补应该按固定标签来写死模板代码时。
我们可以对不确定的位置,定义一个插槽,等待调用者调用该组件的时候再把内容传入。
这样一来组件就拥有了更强的通用性。该插槽插入什么内容完全取决于父组件如何使用。
组件内使用
占位
使用组件时,在组件标签夹着的地方, 传入标签替换到slot
位置上
子组件:
<template>
<h2>{{ title }}</h2>
<div class="content">
<slot></slot>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: "title的默认值"
}
}
}
</script>
父组件:
<template>
<div class="app">
<!-- 1.内容是button -->
<show-message title="哈哈哈">
<button>我是按钮元素</button>
</show-message>
<!-- 2.内容是超链接 -->
<show-message>
<a href="#">百度一下</a>
</show-message>
<!-- 3.内容是一张图片 -->
<show-message>
<img src="@/img/kobe02.png" alt="">
</show-message>
<!-- 4.内容没有传递 -->
<show-message></show-message>
</div>
</template>
<script>
// 组件引入
import ShowMessage from './ShowMessage.vue'
export default {
// 组件注册
components: {
ShowMessage
}
}
</script>
由于使用了插槽,所以可以传递按钮、超链接、图片等任何DOM
元素。组件、普通文本内容都时可以的。
插槽的内容完全取决于父组件使用子组件时传递的元素。
如果父组件没有给插槽传递内容时,我们可以给插槽设置默认内容。
只需要在定义插槽的
标签夹着的位置写上内容,这个内容只会在没有提供插入的内容时,才会显示。
这就是插槽的默认值。
<template>
<div class="content">
<slot>
插槽的默认值
</slot>
</div>
</template>
如果一个组件中含有多个插槽,那父组件传递的内容会渲染到那一个插槽上面呢?
它会把传递的内容同时渲染到三个插槽上,三个插槽获得的内容完全一样。
为了解决这个问题,我们可以给插槽起一个名字。以此来确定父组件传递的内容属于哪一个插槽。
子组件:
<template>
<div class="nav-bar">
<div class="left">
<!-- name属性可以给插槽取一个名字 -->
<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>
<div class="other">
<slot name="default"></slot>
</div>
</template>
<script>
export default {
}
</script>
父组件:
<template>
<nav-bar>
<!-- v-solt指令 指定这个template属于哪一个插槽 -->
<template v-slot:left>
<button>{{ leftText }}</button>
</template>
<!-- v-solt指令 可以简写为# -->
<template #center>
<span>内容</span>
</template>
<template v-slot:right>
<a href="#">登录</a>
</template>
</nav-bar>
</template>
<script>
// 组件引入
import NavBar from './NavBar.vue'
export default {
// 组件注册
components: {
NavBar
},
data() {
return {
leftText: "返回"
}
}
}
</script>
具名插槽顾名思义就是给插槽起一个名字,只需要在
标签上添加name
属性
一个不带 name
属性的slot
,会有隐含的名字 default
父组件给插槽传递内容时,只需要使用标签和
v-solt
指令指定这段内容属于那一个插槽即可
跟 v-on
和 v-bind
一样,v-slot
也有缩写;v-slot
指令的缩写字符为 #
不写死v-slot
指令的值,而是使用一个变量position
去动态指定内容传递给那个插槽。
<template>
<nav-bar>
<template v-slot:[position]>
<a href="#">注册</a>
</template>
</nav-bar>
<button @click=" position = 'left' ">左边</button>
<button @click=" position = 'center' ">中间</button>
<button @click=" position = 'right' ">右边</button>
</template>
<script>
import NavBar from './NavBar.vue'
export default {
components: {
NavBar
},
data() {
return {
position: "center"
}
}
}
</script>
父级模板里的所有内容都是在父级作用域中编译的
子模板里的所有内容都是在子作用域中编译的
其实就一句话,渲染模板的时候,模板中涉及的变量只能读取本组件内定义的Vue
变量。
是无法跨组件读取的。
有时候我们希望插槽可以访问到子组件中的内容,虽然不能直接跨组件访问。
但是Vue
提供了作用域插槽的方式,来完成这个需求。
子组件:
<template>
<div class="tab-control">
<template v-for="(item, index) in titles" :key="item">
<div class="tab-control-item"
:class="{ active: index === currentIndex }"
@click="itemClick(index)">
<slot :row="item">
<span>{{ item }}</span>
</slot>
</div>
</template>
</div>
</template>
<script>
export default {
props: {
titles: {
type: Array,
default: () => []
}
},
data() {
return {
currentIndex: 0
}
},
emits: ["tabItemClick"],
methods: {
itemClick(index) {
this.currentIndex = index
this.$emit("tabItemClick", index)
}
}
}
</script>
父组件:
<template>
<div class="app">
<!-- 1.tab-control: button -->
<tab-control :titles="['衣服', '鞋子', '裤子']"
@tab-item-click="tabItemClick">
<template v-slot:default="props">
<button>{{ props.row }}</button>
</template>
</tab-control>
</div>
</template>
<script>
import TabControl from './TabControl.vue'
export default {
components: {
TabControl
},
data() {
return {
pageContents: [ "衣服列表", "鞋子列表", "裤子列表" ],
currentIndex: 0
}
},
methods: {
tabItemClick(index) {
console.log("app:", index)
this.currentIndex = index
}
}
}
</script>
其实核心部分就是这么使用的:
row
属性。<slot :row="item">
<span>{{ item }}</span>
</slot>
v-slot
指令绑定到没有起名的插槽上,并定义了一个变量props
<template v-slot:default="props">
<button>{{ props.row }}</button>
</template>
这个props
变量,其实就是把对应的
标签上的属性全部封装成了一个对象。
这里的props
就相当于:
{row:item}
只不过这里的item
应该是子组件中的具体值。
这样使用的时候可以自定义目标内容,然后通过props.row
来调用。
如果我们的插槽是默认插槽default
,那么在使用的时候 v-slot
指令的时候,
v-slot:default="slotProps"
可以简写为v-slot="slotProps"。
如果插槽只有默认插槽时,组件的标签可以被当做插槽的模板来使用
<tab-control :titles="['衣服', '鞋子', '裤子']"
@tab-item-click="tabItemClick"
v-slot="props">
<button>{{ props.item }}</button>
</tab-control>
这个时候也就无需编写标签了。