什么是插槽(slot)?
1. 插槽的作用
- 让父组件可以向子组件指定位置插入 html 结构,也是一种组件间通信的方式。
- 适用于: 父组件 => 子组件
2. 插槽的分类
- 默认插槽
- 具名插槽
- 作用域插槽
一、默认插槽
1. 语法规范
父组件中:定义 html 结构
<Category> <div>html结构1</div> </Category>
子组件中:使用 <slot> 标签,标签内容为默认内容(即不传入具体结构时显示)
<template> <div> <!-- 定义插槽 --> <slot>插槽默认内容...</slot> </div> </template>
2. 实例:呈现三种 html 内容
Category.vue(子组件)
- 使用插槽,呈现父组件中的内容
- 注:其实使用 props 和 v-show 也可以实现,但配置的较为繁琐。
<template> <div class="category"> <h3>{{ title }}分类</h3> <slot>没有传递具体结构时,我会出现</slot> </div> </template> <script> export default { name: "myCategory", props: ["title"], }; </script> <style scoped> .category { background-color: skyblue; height: 300px; width: 200px; } h3 { text-align: center; background-color: orange; } img { width: 100%; } </style>
App.vue(父组件)
- 在 data 中准备好要使用的数据
- 在第一个标签中传入图片、第二个标签输出数据、第三个标签中传入视频
<template> <div class="container"> <Category title="美食"> <img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg"/> </Category> <Category title="游戏"> <ul> <li v-for="(g, index) in games" :key="index">{{ g }}</li> </ul> </Category> <Category title="电影"> <video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" ></video> </Category> </div> </template> <script> import Category from "./components/Category.vue"; export default { name: "App", components: { Category }, data() { return { foods: ["火锅", "烧烤", "小龙虾", "西瓜"], games: ["王者荣耀", "APEX", "超级玛丽"], films: ["《活着》", "《超能陆战队》", "《寻梦环游记》", "《扬名立万》"], }; }, }; </script> <style scoped> .container { display: flex; justify-content: space-around; } video { width: 100%; } </style>
二、具名插槽
1、语法规范
父组件中:定义 html 结构,并给不同的插槽
name
命名,这样可以对应的使用多个插槽。
<Category> <template slot="center"> <div>html结构1</div> </template> <template v-slot="footer"> <div>html结构1</div> </template> </Category>
子组件中:使用 <slot> 标签,标签内容为默认内容(即不传入具体结构时显示)。里面的 name 对应父组件中的定义的值。
<template> <div> <slot name="center">默认插槽内容...</slot> <slot name="footer">默认插槽内容...</slot> </div> </template>
2. 实例:呈现三种 html 内容(多个插槽)
Category.vue(子组件)
- 使用插槽,呈现父组件中的内容
- 每一个插槽都有特定的名字
<template> <div class="category"> <h3>{{ title }}分类</h3> <slot name="center">没有传递具体结构时,我会出现</slot> <slot name="footer">没有传递具体结构时,我会出现</slot> </div> </template> <script> export default { name: "myCategory", props: ["title"], }; </script> <style scoped> .category { background-color: skyblue; height: 300px; width: 200px; } h3 { text-align: center; background-color: orange; } img { width: 100%; } </style>
App.vue(父组件)
- 在 data 中准备好要使用的数据
- 在第一个标签中传入图片、第二个标签输出数据、第三个标签中传入视频
- 并在第一个插槽下面,呈现第二个插槽对应的内容(超链接 和 h4文本)
<template> <div class="container"> <Category title="美食"> <img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" /> <a slot="footer" href="#">更多美食</a> </Category> <Category title="游戏"> <ul slot="center"> <li v-for="(g, index) in games" :key="index">{{ g }}</li> </ul> <div class="foot" slot="footer"> <a href="#">单机游戏</a> <a href="#">网络游戏</a> </div> </Category> <Category title="电影"> <video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" ></video> <template v-slot:footer> <div class="foot"> <a href="#">经典</a> <a href="#">热门</a> <a href="#">推荐</a> </div> <h4>欢迎前来观影</h4> </template> </Category> </div> </template> <script> // 引入组件 import Category from "./components/Category.vue"; export default { name: "App", components: { Category }, data() { return { foods: ["火锅", "烧烤", "小龙虾", "西瓜"], games: ["王者荣耀", "APEX", "超级玛丽"], films: ["《活着》", "《超能陆战队》", "《寻梦环游记》", "《扬名立万》"], }; }, }; </script> <style scoped> .container, .foot { display: flex; justify-content: space-around; } video { width: 100%; } h4 { text-align: center; } </style>
三、作用域插槽
1. 理解
数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
在下面实例中:games 数据在 Category 组件中,但使用数据所遍历出来的结构由 App 组件决定。
2. 语法规范
父组件中:定义 html 结构。数据不在父组件上。
<Category> <template> <ul> <li v-for="g in scopeData.games" :key="g">{{g}}</li> </ul> </template> <template> <ul> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </ul> </template> </Category>
子组件中:使用 <slot> 标签,标签内容为默认内容(即不传入具体结构时显示)
<template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name: 'Category', props: ['title'], //数据在子组件自身 data(){ return{ games: ["王者荣耀", "侠盗飞车", "超级玛丽"] } } } </script>
2. 实例:对游戏数据的三种呈现(有序、无序和h4大小)
Category.vue(子组件)
- 使用插槽,呈现父组件中的内容
- 数据放入了子组件中
- 单项数据绑定 games
<template> <div class="category"> <h3>{{ title }}分类</h3> <slot :games="games"></slot> </div> </template> <script> export default { name: "myCategory", props: ["title"], data() { return { games: ["王者荣耀", "侠盗飞车", "超级玛丽"], }; }, }; </script> <style scoped> .category { background-color: skyblue; height: 300px; width: 200px; } h3 { text-align: center; background-color: orange; } img { width: 100%; } </style>
App.vue(父组件)
常规写法:先 scope="demo",再 demo.games
ES6+另一种常规写法写法:先 slot-scope="{games}",直接解构 games
ES6+新命名方式:v-slot="games",直接解构 games
3可以简写为:#:{games}
<template> <div class="container"> <Category title="游戏"> <template scope="demo"> <ul> <li v-for="(g, index) in demo.games" :key="index">{{ g }}</li> </ul> </template> </Category> <Category title="游戏"> <template slot-scope="{games}"> <ol> <li v-for="(g, index) in games" :key="index">{{ g }}</li> </ol> </template> </Category> <Category title="游戏"> <template v-slot="{games}"> <h4><li v-for="(g, index) in games" :key="index">{{ g }}</li></h4> </template> </Category> </div> </template> <script> // 引入组件 import Category from "./components/Category.vue"; export default { name: "App", components: { Category }, }; </script> <style scoped> .container, .foot { display: flex; justify-content: space-around; } video { width: 100%; } h4 { text-align: center; } </style>
不积跬步无以至千里 不积小流无以成江海