在实际开发中,子组件往往只提供基本的交互功能,而内容是有父组件来提供的。为此,Vue.js 提供了一种混合父组件内容和子组件模板的方式,这种方式称为内容分发。
1、基本用法
Vue.js 参照当前 Web Components 规范草案实现了一套内容分发的 API,使用 <slot> 元素作为原始内容的插槽。
【实例】插槽的基本用户。
(1)创建 ParentComponent.vue 父组件
<template>
<fieldset>
<legend>父组件</legend>
<h3>父组件内容:插槽的使用</h3>
<!-- 第三步:使用组件 -->
<BlogSlot>
<p>博客信息:{{ blogName }}</p>
<p>博客信息:{{ blogUrl }}</p>
</BlogSlot>
</fieldset>
</template>
<script>
//第一步:引用组件
import BlogSlot from '@/components/BlogSlot.vue'
export default {
data() {
return {
blogName: '您好,欢迎访问 pan_junbiao的博客',
blogUrl: 'https://blog.csdn.net/pan_junbiao'
}
},
//第二步:注册组件
components: {
BlogSlot,
}
}
</script>
(2)创建 BlogSlot.vue 子组件
<template>
<fieldset>
<legend>子组件</legend>
<!-- 核心代码:使用插槽 -->
<slot></slot>
</fieldset>
</template>
<style scoped>
fieldset {
font-size: 18px;
color: blue;
}
</style>
(3)在 App.vue 根组件中,引入父组件
<template>
<!-- 第三步:使用组件 -->
<ParentComponent />
</template>
<script>
//第一步:引用组件
import ParentComponent from '@/components/ParentComponent.vue'
export default {
//第二步:注册组件
components: {
ParentComponent,
}
}
</script>
<style></style>
执行结果:
由渲染结果可以看出,父组件中的内容会被代替子组件中的 <slot> 标签,这样就可以在不同地方使用子组件的机构并且填充不同的父组件内容,从而提供组件的复用性。
2、编译作用域
上述实例代码在父组件中调用 <BlogSlot> 子组件,并绑定了父组件中的数据:blogName、blogUrl。其中的数据只能在父组件的作用域下进行解析,而不能在 <BlogSlot> 子组件的作用域下进行解析。也就是说,父组件模板里的所有内容都是在父组件作用域中编译的;子组件模板里的所有内容都是在子组件作用域中编译的。
3、默认内容
有些时候需要为一个插槽设置默认内容,该内容只会在没有提供内容的时候被渲染。
【实例】为插槽设置默认内容。
(1)修改 ParentComponent.vue 父组件,将插槽的内容注释掉
<template>
<fieldset>
<legend>父组件</legend>
<h3>父组件内容:插槽的使用</h3>
<!-- 第三步:使用组件 -->
<BlogSlot>
<!-- 父组件中,将插槽的内容注释掉 -->
<!-- <p>博客信息:{{ blogName }}</p> -->
<!-- <p>博客信息:{{ blogUrl }}</p> -->
</BlogSlot>
</fieldset>
</template>
(2)修改 BlogSlot.vue 子组件,为插槽设置默认内容
<template>
<fieldset>
<legend>子组件</legend>
<!-- 核心代码:使用插槽,并设置默认内容 -->
<slot>默认内容</slot>
</fieldset>
</template>
执行结果:
4、命名插槽
如果要在组件模板中使用多个插槽,就需要到 <slot> 标签的 name 属性。通过这个属性可以为插槽命令。在向命令的插槽提供内容时,可以在一个 <template> 元素上使用 v-slot 指令,将插槽的名称作为 v-slot 指令的参数。这样,<template> 元素中的所有内容都将被传入相应的插槽。
【实例】使用命名插槽。
(1)修改 ParentComponent.vue 父组件
<template>
<fieldset>
<legend>父组件</legend>
<h3>父组件内容:插槽的使用</h3>
<!-- 第三步:使用组件 -->
<BlogSlot>
<!-- v-slot指令的参数需要与子组件中的slot元素的name值匹配 -->
<template v-slot:blogName>
<h3>插槽内容1:{{ blogName }}</h3>
</template>
<template v-slot:blogUrl>
<h4>插槽内容2:{{ blogUrl }}</h4>
</template>
</BlogSlot>
</fieldset>
</template>
<script>
//第一步:引用组件
import BlogSlot from '@/components/BlogSlot.vue'
export default {
data() {
return {
blogName: '您好,欢迎访问 pan_junbiao的博客',
blogUrl: 'https://blog.csdn.net/pan_junbiao'
}
},
//第二步:注册组件
components: {
BlogSlot,
}
}
</script>
(2)修改 BlogSlot.vue 子组件
<template>
<fieldset>
<legend>子组件</legend>
<!-- 核心代码:使用插槽 -->
<slot name="blogName"></slot>
<hr>
<slot name="blogUrl"></slot>
</fieldset>
</template>
<style scoped>
fieldset {
font-size: 18px;
color: blue;
}
</style>
执行结果:
5、作用域插槽
有些时候需要让插槽内容能够访问子组件中才有的数据。为了让子组件中的数据在父组件的插槽内容中可用,可以将子组件中的数据作为一个 <slot> 标签的属性并对其进行绑定。绑定在 <slot> 标签上的属性被称为插槽 Prop。然后在父组件作用域中,可以为 v-slot 设置包含所有插槽 Prop 的对象的名称。
【实例】单个插槽时,使用作用域插槽。
(1)修改 ParentComponent.vue 父组件
<template>
<fieldset>
<legend>父组件</legend>
<h3>父组件内容:插槽的使用</h3>
<!-- 第三步:使用组件 -->
<BlogSlot v-slot="slotProps">
<p>用户名称:{{ slotProps.userName }}</p>
<p>用户年龄:{{ slotProps.age }}</p>
<p>用户性别:{{ slotProps.sex }}</p>
<p>博客信息:{{ slotProps.blogName }}</p>
<p>博客信息:{{ slotProps.blogUrl }}</p>
</BlogSlot>
</fieldset>
</template>
<script>
//第一步:引用组件
import BlogSlot from '@/components/BlogSlot.vue'
export default {
//第二步:注册组件
components: {
BlogSlot,
}
}
</script>
(2)修改 BlogSlot.vue 子组件
<template>
<fieldset>
<legend>子组件</legend>
<!-- 核心代码:使用插槽 -->
<slot :userName="userName" :age="age" :sex="sex" :blogName="blogName" :blogUrl="blogUrl"></slot>
</fieldset>
</template>
<script>
export default {
data() {
return {
userName: 'pan_junbiao的博客',
age: 36,
sex: '男',
blogName: '您好,欢迎访问 pan_junbiao的博客',
blogUrl: 'https://blog.csdn.net/pan_junbiao'
}
}
}
</script>
<style scoped>
fieldset {
font-size: 18px;
color: blue;
}
</style>
执行结果:
上述代码中,将子组件中的数据 userName、age、sex、blogName、blogUrl 作为 <slot> 标签绑定的属性,然后在父组件作用域中,为 v-slot 设置的包含所有插槽 Prop 的对象的名称为 slotProps,在通过 {{ slotProps.userName }}、{{ slotProps.age }} 等即可访问子组件中的数据。
【实例】多个插槽时(命名插槽),使用作用域插槽。
(1)修改 ParentComponent.vue 父组件
<template>
<fieldset>
<legend>父组件</legend>
<h3>父组件内容:插槽的使用</h3>
<!-- 第三步:使用组件 -->
<BlogSlot>
<!-- v-slot指令的参数需要与子组件中的slot元素的name值匹配 -->
<template v-slot:userInfo="slotProps">
<p>用户名称:{{ slotProps.userName }}</p>
<p>用户年龄:{{ slotProps.age }}</p>
<p>用户性别:{{ slotProps.sex }}</p>
</template>
<template v-slot:blogInfo="slotProps">
<p>博客信息:{{ slotProps.blogName }}</p>
<p>博客信息:{{ slotProps.blogUrl }}</p>
</template>
</BlogSlot>
</fieldset>
</template>
<script>
//第一步:引用组件
import BlogSlot from '@/components/BlogSlot.vue'
export default {
//第二步:注册组件
components: {
BlogSlot,
}
}
</script>
(2)修改 BlogSlot.vue 子组件
<template>
<fieldset>
<legend>子组件</legend>
<!-- 核心代码:使用插槽 -->
<slot name="userInfo" :userName="userName" :age="age" :sex="sex"></slot>
<hr>
<slot name="blogInfo" :blogName="blogName" :blogUrl="blogUrl"></slot>
</fieldset>
</template>
<script>
export default {
data() {
return {
userName: 'pan_junbiao的博客',
age: 36,
sex: '男',
blogName: '您好,欢迎访问 pan_junbiao的博客',
blogUrl: 'https://blog.csdn.net/pan_junbiao'
}
}
}
</script>
<style scoped>
fieldset {
font-size: 18px;
color: blue;
}
</style>
执行结果: