Vue 插槽
-
环境准备(就一个 分类组件)
### Category.vue <template> <div class="category"> <h3>xxx分类</h3> <ul> <li>11111</li> <li>2222</li> <li>3333</li> <li>4444</li> </ul> </div> </template> <script> export default { name:'Category' } </script> <style scoped> h3 { color: orange; text-align: center; } ul { margin-left: 40px; } .category { background: skyblue; width: 200px; height: 300px; } </style> ### App.vue <template> <div class="container"> <Category/> <!--使用3次--> <Category/> <Category/> </div> </template> <script> import Category from './components/slot/Category.vue' export default { name: 'App', components: { Category, } } </script> <style> .container { display: flex; justify-content: space-around; } </style>
-
把list数据放在App中,父组件向子组件传值,现在这么搞
### app.vue <template> <div class="container"> <!--传两组数据: title && listData--> <Category title='foods' :listData="foods" /> <Category title='films' :listData="films" /> <Category title='games' :listData="games" /> </div> </template> <script> import Category from './components/slot/Category.vue' export default { name: 'App', components: { Category, }, data(){ return { // 已准备好的数据 foods:['banana','apple','pear'], films:['Speed Star','Harry Polly','Ship'], games:['cross fire','asktao','QQSpeed'] } } } </script> <style> ...... </style> ### Category.vue <template> <!--渲染数据--> <div class="category"> <h3>{{title}}</h3> <ul> <li v-for="(item,index) in listData" :key="index">{{item}}</li> </ul> </div> </template> <script> export default { name:'Category', props:['title','listData'] // 接收 } </script> <style scoped> ...... </style> - 至此,第一种效果完成,没什么可以讲的
-
现在,改变需求,Foods列表项变成一张图片,Films列表项变成一个视频,Games不变
可以这么干,添加一个判断条件即可
### Category.vue <template> <div class="category"> <h3>{{title}}</h3> <ul v-show="title !== 'Foods' && title !== 'Films' "> <!--添加判断--> <li v-for="(item,index) in listData" :key="index">{{item}}</li> </ul> <!--两个判断--> <img v-show="title == 'Foods' " src="https://img1.baidu.com/it/u=1294439722,116143144&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500" > <img v-show="title == 'Films' " src="https://img2.baidu.com/it/u=2281104515,2554096595&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=714" > </div> </template>
-
如果项目需求少,这种方式没有问题.但缺点也很明显,一旦列表项多了,处理起来繁琐,得一个个去判断,很耗时间
现在使用'Vue提供的插槽'来实现这一需求(插槽就是'占坑',等待组件的使用者[可以理解为父组件]填坑)
### App.vue <template> <div class="container"> <!--不再传listData--> <!-- <Category title='Foods' :listData="foods"> --> <!--Category组件里面包裹html元素,这里包裹图片--> <Category title='Foods'> <img src="https://img1.baidu.com/it/u=1294439722,116143144&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500" > </Category> <!--这里包裹视频--> <Category title='Films'> <video controls src="https://xxx.mp4"></video> </Category> <!--这里渲染数据--> <Category title='Games'> <ul> <!-- <li v-for="(item,index) in listData" :key="index">{{item}}</li> --> <li v-for="(item,index) in games" :key="index">{{item}}</li> </ul> </Category> </div> </template> <script> import Category from './components/slot/Category.vue' export default { name: 'App', components: { Category, }, data(){ return { foods:['banana','apple','pear'], films:['Speed Star','Harry Polly','Ship'], games:['cross fire','asktao','QQSpeed'] } } } </script> <style> .container { display: flex; justify-content: space-around; } video { width: 100%; } </style> ### Category.vue <template> <div class="category"> <h3>{{title}}</h3> <!--如果父组件有传数据(html结构)过来,就展示数据;如果没传,就展示默认的数据--> <!--占坑,等待接收数据--> <slot>我是一些默认值(没传值的时候我就显示)</slot> </div> </template> <script> export default { name:'Category', // props:['title','listData'] // 不再接收 ListData props:['title'] } </script> <style scoped> ...... </style>
-
至此,插槽的用途就是: 复用组件的时候,利用插槽可以渲染不同的数据结构(多数据的时候用起来很方便,数据少就使用If判断即可)
具名插槽演示(上面的基础上,取个名字而已,以便插入不同的数据结构)
### Category.vue
<template>
<div class="category">
<h3>{{title}}</h3>
<!--就加了一个name属性-->
<slot name="center">我是一些默认值(没传值的时候我就显示center)</slot>
<slot name="footer">我是一些默认值(没传值的时候我就显示footer)</slot>
</div>
</template>
### App.vue
<template>
<div class="container">
<Category title='Foods'>
<!--把img标签结构插入center插槽-->
<img slot="center" src="https://img1.baidu.com/it/u=1294439722,116143144&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500" >
<!--把a标签结构插入footer插槽-->
<a slot="footer" href="">美食连接</a>
</Category>
<!--以下同理-->
<Category title='Films'>
<video slot="center" controls src="...mp4"></video>
<a slot="footer" href="">电影连接</a>
<a slot="footer" href="">yyy连接</a>
</Category>
<Category title='Games'>
<ul slot="center">
<!-- <li v-for="(item,index) in listData" :key="index">{{item}}</li> -->
<li v-for="(item,index) in games" :key="index">{{item}}</li>
</ul>
<a slot="footer" href="">游戏连接</a>
</Category>
</div>
</template>
-
往一个插槽插入多个数据结构,可以这么搞
- 写法一:多次使用 ### App.vue ...... <Category title='Films'> ...... <!--使用多次--> <a slot="footer" href="">电影连接</a> <a slot="footer" href="">yyy连接</a> </Category> - 写法二: 使用 <template slot> ...... <Category title='Films'> <template slot="footer"> <!--slot属性写在template里--> <a href="">电影连接</a> <a href="">yyy连接</a> </template> </Category> - 写法三: 使用<template v-slot>,这是新的写法 ...... <Category title='Films'> <!-- <template slot="footer"> --> <template v-slot:footer> <a href="">电影连接</a> <a href="">yyy连接</a> </template> </Category>
插槽作用域
- 环境准备(Category渲染三个list,数据源要求在Category中,要求三个list的css样式不一样)
### App.vue
<template>
<div class="container">
<!--渲染三组list-->
<Category title='Games'></Category>
<Category title='Games'></Category>
<Category title='Games'></Category>
</div>
</template>
<script>
import Category from './components/slot/Category.vue'
export default {
name: 'App',
components: {
Category,
},
data(){
return {
// 不再返回数据
}
}
}
</script>
<style scoped>
.container {
display: flex;
justify-content: space-around;
}
</style>
### Category
<template>
<div class="category">
<h3>{{title}}</h3>
<!--渲染数据-->
<ul>
<li v-for="(game,index) in games" :key="index">{{game}}</li>
</ul>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
data(){
return {
games:['cross fire','asktao','QQSpeed'] // 数据放在这
}
}
}
</script>
<style scoped>
h3 {
font-weight: bold;
background: orange;
text-align: center;
}
ul {
margin-left: 40px;
}
img {
width: 100%;
}
.category {
background: skyblue;
width: 200px;
height: 300px;
}
a {
display: inline-block;
width: 168px;
text-align: center;
}
video {
width: 100%;
}
</style>
- 直接上代码
### Cagegory.vue
<template>
<div class="category">
<h3>{{title}}</h3>
<!--把games数据传给组件使用者(父组件)-->
<slot :games="games">我是默认值</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
data(){
return {
games:['cross fire','asktao','QQSpeed'] // 数据源
}
}
}
</script>
<style scoped>
......
</style>
### App.vue
<template>
<div class="container">
<Category title='Games'>
<!--想要接收数据,必须使用template包裹起来-->
<!--使用scope属性来接收子组件传过来的数据-->
<!--该数据是一个对象(字典)-->
<template scope="dataObj">
<!-- {{dataObj}} -->
<!--dataObj.games对应子组件的list-->
<ul>
<li v-for="(game,index) in dataObj.games" :key="index">{{game}}</li>
</ul>
</template>
</Category>
<Category title='Games'>
<!--也可以使用es6的写法-->
<template scope="{games}">
<ol>
<!--上面是遍历dataObj.games,这里遍历games即可-->
<li v-for="(game,index) in games" :key="index">{{game}}</li>
</ol>
</template>
</Category>
<Category title='Games'>
<!--还可以这么写,新的写法-->
<template slot-scope="{games}">
<h4 v-for="(game,index) in games" :key="index">{{game}}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/slot/Category.vue'
export default {
name: 'App',
components: {
Category,
},
}
</script>
<style scoped>
......
</style>
插槽小结
-
作用: 让父组件可以向子组件指定位置插入html结构,也是组件之间的一种通信方式
适用于父组件==>子组件
-
分类: 默认插槽/具名插槽/作用域插槽
-
使用方式1: 默认插槽
- 父组件中: <Category> <div>html结构</div> </Category> - 子组件中: <template> <div> <!--定义插槽--> <slot>插槽默认内容</slot> </div> </template>
-
使用方式2:具名插槽
- 父组件中: <Category> <template slot="center"> <div>html结构</div> </template> </Category> - 子组件中: <template> <div> <!--定义插槽--> <slot name="center">插槽默认内容</slot> </div> </template>
-
使用方式3:作用域插槽
- 理解: 数据在组件的自身,但根据数据生成的结构 需要组件使用者来决定(父组件决定). 例如: games数据在Cagegory组件中,但使用数据所遍历出来的结构由App组件决定
### App.vue <Category title='Games'> <template scope="dataObj"> <ul> <li v-for="(game,index) in dataObj.games" :key="index">{{game}}</li> </ul> </template> </Category> <Category title='Games'> <template scope="{games}"> <ol> <li v-for="(game,index) in games" :key="index">{{game}}</li> </ol> </template> </Category> ### Category.vue <template> <div class="category"> <h3>{{title}}</h3> <slot :games="games">我是默认值</slot> </div> </template>