什么是插槽?
Vue为组件的封装者提供了插槽(slot),插槽是指开发者在封装组件时不确定的、希望由组件的使用者指定的部分。也就是说,插槽是组件封装期间为组件的使用者预留的占位符,允许组件的使用者在组件内展示特定的内容。通过插槽,可以使组件更灵活、更具有可复用性。
插槽需要定义后才能使用,下面对定义插槽和使用插槽分别进行讲解。
1. 定义插槽
在封装组件时,可以通过<slot>标签定义插槽,从而在组件中预留占位符。假设项目中有一个MyButton组件,在MyButton组件中定义插槽的示例代码如下。
<template>
<button>
<slot></slot>
</button>
</template>
在<slot>标签内可以添加一些内容作为插槽的默认内容。如果组件的使用者没有为插槽提供任何内容,则默认内容生效;如果组件的使用者为插槽提供了插槽内容,则该插槽内容会取代默认内容。
另外,如果一个组件没有预留任何插槽,则组件的使用者提供的任何插槽内容都会不起作用。
2. 使用插槽
使用插槽即在父组件中使用子组件的插槽,在使用时需要将子组件写成双标签的形式,在双标签内提供插槽内容。例如,使用MyButton组件的插槽的示例代码如下。
<template>
<MyButton>
按钮
</MyButton>
</template>
因为插槽内容是在父组件模板中定义的,所以在插槽内容中可以访问到父组件的数据。插槽内容可以是任意合法的模板内容,不局限于文本。例如,可以使用多个元素或者组件作为插槽内容,示例代码如下。
<MyButton>
<span style="color: yellow;">按钮</span>
<MyLeft />
</MyButton>
演示插槽的使用方法
创建src\components\SlotSubComponent.vue文件,用于展示子组件的相关内容。
<template>
<div>测试插槽的组件</div>
<slot></slot>
</template>
创建src\components\MySlot.vue文件,用于展示插槽的相关内容。
<template>
父组件-----{{ message }}
<hr>
<SlotSubComponent>
<p>{{ message }}</p>
</SlotSubComponent>
</template>
<script setup>
import SlotSubComponent from './SlotSubComponent.vue'
const message = '这是组件的使用者自定义的内容'
</script>
演示插槽的默认内容
演示插槽的默认内容,实现当组件的使用者没有自定义内容时默认内容生效的效果。
注释MySlot组件中插槽内容。
<!-- <p>{{ message }}</p> -->
在SlotSubComponent组件中为<slot>标签提供默认内容。
<slot>
<p>这是默认内容</p>
</slot>
什么是具名插槽?
在Vue中当需要定义多个插槽时,可以通过具名插槽来区分不同的插槽。具名插槽是给每一个插槽定义一个名称,这样就可以在对应名称的插槽中提供对应的数据了。
插槽通过<slot>标签来定义,<slot>标签有一个name属性,用于给各个插槽分配唯一的名称,以确定每一处要渲染的内容。添加name属性的<slot>标签可用来定义具名插槽。
定义具名插槽的语法格式如下。
<slot name="插槽名称"></slot>
在父组件中,如果要把内容填充到指定名称的插槽中,可以通过一个包含v-slot指令的<template>标签来实现,语法格式如下。
<组件名>
<template v-slot:插槽名称></template>
</组件名>
与v-on和v-bind类似,v-slot也有简写形式,即把v-slot:替换为#。例如,v-slot:title可以简写为#title。
演示具名插槽的使用
创建src\components\ArticleInfo.vue文件,用于展示文章内容模板。
<template>
<div class="article-container">
<div class="header-box"><slot name="header"></slot></div>
<div class="content-box"><slot name="content"></slot></div>
<div class="footer-box"><slot name="footer"></slot></div>
</div>
</template>
<style>
.article-container > div { border: 1px solid black; }
</style>
创建src\components\MyArticle.vue文件,用于提供文章数据,在MyArticle组件中导入并使用ArticleInfo组件,并在<ArticleInfo>标签中为不同插槽添加不同的信息。
<template>
<ArticleInfo>
<template v-slot:header><p>这是文章的头部区域</p></template>
<template v-slot:content><p>这是文章的内容区域</p></template>
<template #footer><p>这是文章的尾部区域</p></template>
</ArticleInfo>
</template>
<script setup>import ArticleInfo from './ArticleInfo.vue' </script>
什么是作用域插槽?
一般情况下,在父组件中不能使用子组件中定义的数据。如果想要在父组件中使用子组件中定义的数据,则需要通过作用域插槽来实现。作用域插槽是带有数据的插槽,子组件提供一部分数据给插槽,父组件接收子组件的数据进行页面渲染。
作用域插槽的使用分为定义数据和接收数据两个部分。
1. 定义数据
在封装组件的过程中,可以为预留的插槽定义数据,供父组件接收并使用子组件中的数据。在作用域插槽中,可以将数据以类似传递props属性的形式添加到<slot>标签上。
例如,在封装MyHeader组件时,在插槽中定义数据供父组件使用,示例代码如下。
<slot message="Hello Vue.js"></slot>
2. 接收数据
使用默认插槽和具名插槽接收数据的方式不同。
下面讲解默认插槽:
在Vue中,每个插槽都有name属性,表示插槽的名称。在定义插槽时虽然省略了<slot>标签的name属性,但是name属性默认为default,这样的插槽属于默认插槽。
在父组件中可以通过v-slot指令接收插槽中定义的数据,即接收作用域插槽对外提供的数据。通过v-slot指令接收到的数据可以在插槽内通过Mustache语法进行访问。
例如,在父组件中使用MyHeader组件中的插槽时,通过v-slot指令的值接收传递的数据的示例代码如下。
<MyHeader v-slot="scope">
<p>{{ scope.message }}</p>
</MyHeader>
通过v-slot接收从作用域插槽中传递的数据,scope为形参,表示从作用域插槽中接收的数据,该形参的名称可以自定义。
作用域插槽对外提供的数据对象可以使用解构赋值以简化数据的接收过程,示例代码如下。
<MyHeader v-slot="{ message }">
<p>{{ message }}</p>
</MyHeader>
通过解构赋值解构对象,解构后子组件中定义的数据可以直接访问,而不是以“形参.属性”的方式访问。
下面讲解具名插槽:
在Vue中,通过<slot>标签添加name属性来定义具名插槽,在具名插槽中也可以向父组件中传递数据。
例如,在封装MyHeader组件时,向具名插槽中传入数据的语法格式如下。
<slot name="header" message="hello"></slot>
具名插槽和作用域插槽可以作用在同一个<slot>标签上且并不冲突。<slot>标签的name属性不会作为数据传递给插槽,所以最终传递给组件的数据只有message属性。
在使用具名插槽时,插槽属性可以作为v-slot的值被访问到,基本语法格式为“v-slot:插槽名称="形参"”,简写形式为“#插槽名称="形参"”,使用简写形式使用插槽的示例代码如下。
<MyHeader>
<template #header="{ message }">
{{ message }}
</template>
</MyHeader>
如果在一个组件中同时定义了默认插槽和具名插槽,并且它们均需要为父组件提供数据,这时就需要为默认插槽使用显式的<template>标签来接收数据,示例代码如下。
<MyHeader>
<template #default="{ message }">
{{ message }}
</template>
</MyHeader>
演示作用域插槽的使用
创建src\components\SubScopeSlot.vue文件,用于展示作用域插槽。
<template>
<slot message="Hello 默认插槽"></slot>
<hr>
<slot message="Hello Vue.js" name="header"></slot>
<hr>
<slot :user="user" name="content"></slot>
</template>
<script setup>
import { reactive } from 'vue'
const user = reactive({ name: 'xiaoyuan', age: '15' })
</script>
创建src\components\ScopeSlot.vue文件,用于为作用域插槽提供数据。
<template>
<SubScopeSlot>
<template v-slot:default="scope"><p>{{ scope }}</p></template>
<template v-slot:header="scope">
<p>{{ scope }}</p><p>{{ scope.message }}</p>
</template>
<template #content="{ user }">
<p>{{ user.name }}</p><p>{{ user.age }}</p></template>
</SubScopeSlot>
</template>
<script setup>import SubScopeSlot from './SubScopeSlot.vue'</script>