在实际项目,我们不仅仅是单纯的在父组件中引入子组件,有时候还得根据父组件的需求来动态更改子组件的内容和样式。虽然有很多的方法可以来实现这个步骤,比如使用父子组件传值来实现父组件动态更改子组件中的数据,进而改变内容和样式,但是这种方式并不是很直观。
为此,在这里介绍Vue中的一个知识点:插槽(slot),插槽其实就是一段模块,在父组件中定义模块,在子组件中选择任意位置放置(是不是很有趣,以前都是在父组件中引入子组件,然后在父组件中选择位置放置,但现在却反过来了)。
模板种类大体可以分为两类:
1.非插槽模板,也就是常规的html标签,如<template>标签中放置的<div>、<select>、<button>等标签,都是已经固定好了展示位置的;
2.插槽模板,这套模板是在父组件中定义的,在子组件中使用。不过,你可以在子模板<template>中选择任意位置放置插槽,还可以放置多次,此外,可以在模块中加入名字属性,就可以使之成为具名插槽,在模板中加入'slot-scope'(Vue3中已改为'v-slot')获取从子组件中传入的数据,使之成为作用域插槽,这样可以很好地解决父组件决定插槽的样式,子组件提供相应内容的需求。
为了方便大家理解,我将每种插槽全部分开讲解,请大家记得详看代码中的注释,项目核心结构如下(3种插槽结构都一样,只展示变化了的文件):
1.默认插槽(也叫匿名插槽,或单个插槽),先上效果图:
文件代码如下:
Child.vue
<template>
<div class="Child">
<h1>我是子组件</h1>
//这个地方才是真正渲染插槽的地方,也就是<p>默认插槽</p>
//所以'默认插槽'这四个字才会是绿色
<slot />
</div>
</template>
<style scoped>
.Child{
color: green;
}
</style>
Father.vue
<template>
<div class="hello">
<h1>我是父组件</h1>
//这个地方也可以写成<ChildComponent><p>默认插槽</p> </ChildComponent>
//因为在template中child-component可以解析成ChildComponent(看一个大佬说过,这个标签的源码是用正则表达式匹配的)
<child-component>
//这个地方虽然看似是在父组件中放置了<p>默认插槽</p>
//但是它是在子组件中进行解析的
//大家可以看一下文字的颜色,如果是直接在父组件中渲染,那么应该是红色
//但是最终的结果却是绿色,表明这个模块其实是属于子组件的
<p>默认插槽</p>
</child-component>
</div>
</template>
<script>
import ChildComponent from './Child'
export default {
name: 'Father',
components:{
ChildComponent
}
}
</script>
<style scoped>
.hello{
color:red
}
</style>
router/index.js(这部分代码匿名插槽、具名插槽和定义域插槽共用)
import Vue from 'vue'
import Router from 'vue-router'
import Father from '@/components/Father'
Vue.use(Router)
const routes = [
{
path: '/',
name: 'Father',
component: Father
}
]
const routers = new Router({
//别问我为什么不用默认的hash模式,问就是我觉得hash模式在路由中加的'#'不好看,哈哈
mode: 'history',
routes
})
export default routers
2.具名插槽,先上效果图:
文件代码如下:
Child.vue
<template>
<div class="Child">
<h1>我是子组件</h1>
<slot name="BigSlot"></slot>
<slot name="SmallSlot"></slot>
<slot name="SmallSlot"></slot>
<slot name="SmallSlot"></slot>
</div>
</template>
<style scoped>
.Child{
color: green;
}
//这里的 .big 和 .small 的样式放在父组件中同样生效
.big{
font-size: 50px;
}
.small{
font-size: 20px;
}
</style>
Father.vue
<template>
<div class="hello">
<h1>我是父组件</h1>
<child-component>
//下面这部分标签全部都会在子组件中渲染,通过slot属性设置相应的名字,即可在子组件中使用
<p class="big" slot="BigSlot">我是大插槽</p>
<p class="small" slot="SmallSlot">我是小插槽</p>
</child-component>
</div>
</template>
<script>
import ChildComponent from './Child'
export default {
name: 'Father',
components:{
ChildComponent
}
}
</script>
<style scoped>
.hello{
color:red
}
</style>
3.定义域插槽,先上效果图:
这里要给各位提个醒:一定要注意父组件获取到的子组件中数据的效果,强烈建议大家都亲手试一下,不然光看有时候是没用的
当前的Father.vue
<template>
<div class="hello">
<h1>我是父组件</h1>
<child-component>
//一定要注意这个传输过来的data数据的值
<div slot-scope="data">
<p>子组件传输过来的数据</p>
<p>{{ data }}</p>
</div>
</child-component>
</div>
</template>
<script>
import ChildComponent from './Child'
export default {
name: 'Father',
components:{
ChildComponent
}
}
</script>
<style scoped>
.hello{
color:red
}
</style>
我最开始想做的效果如下:
但是最开始没注意数据的传输格式,所以浪费了一些时间
现在的Father.vue
<template>
<div class="hello">
<h1>我是父组件</h1>
<child-component>
//最新版本使用的是 v-slot 这个标签
<div slot-scope="data">
<p>姓名:{{ data.person.name }}</p>
<p>身份:{{ data.person.identity }}</p>
<p>技能:{{ data.person.skill }}</p>
<p>憎恨:{{ data.person.hate }}</p>
<p>名言: {{ data.person.quotes }}</p>
</div>
</child-component>
</div>
</template>
<script>
import ChildComponent from './Child'
export default {
name: 'Father',
components:{
ChildComponent
}
}
</script>
<style scoped>
.hello{
color:red
}
</style>
Child.vue
<template>
<div class="Child">
<h1>我是子组件</h1>
<slot :person="person"></slot>
</div>
</template>
<script>
export default {
name: 'Child',
data(){
return{
person:{
name: '马保国',
identity: '浑元形意太极门掌门人',
skill: '五鞭太极拳',
hate: '不讲武德',
quotes: '年轻人不讲武德,耗子尾汁'
}
}
}
}
</script>
<style scoped>
.Child{
color: green;
}
</style>
在定义域插槽中,我们可以看到绑定数据的插槽,显示内容完全由子组件决定,数据来自子组件。
多么美妙的知识呀,虽然感觉头发蓬蓬的能起飞,但是感觉自己还能刚呢,各位小伙伴,一起加油吧,奥利给!