前言
学习和使用slot的笔记记录
一、插槽有什么作用
首先我们来里了解一下插槽是做什么的,他解决了什么问题!
我们在开发中,往往会把一个复杂的页面分为多个组件去组合,或者当复用性比较高的也会提取为一个组件,但是当我们设计的组件还不能在某些特殊的情况满足我们的需求的时候,我们还需要添加额外的时候,那么该怎么办呢?总不可能再写多一个组件吧?这样的话,复用性也太低了。所以插槽就出现了。
当实际使用的组件不能完全的满足我们的需求的时候,我们就可以用插槽来分发内容,往我们的这个组件中添加一点东西。
二、插槽的使用
基本使用
我们在父组件中这样写,我们会发现添加在子组件里闭合的内容并不会展示出来
//子组件B
const BBorder = {
template: `<div>我是儿子B</div>`,
}
const Parents = {
methods: {
},
//实例化BBorder这个子组件
template: `<div>我是父组件:{{msg}}
<BBorder>无敌牛逼</BBorder>
</div>`,
//注册两个子组件
components: {
BBorder
}
}
//实例化一个vue,并且绑定到app这个元素
const vm = new Vue({
el: '#app',
components: {
Parents,
}
})
输出效果如图:
我们可以看到的是我们添加的“无敌牛逼”并没有输出来,但是如果我们改为下面的形式,给子组件添加上slot,就能把我们插入的内容显示。
//子组件B
const BBorder = {
template: `<div>我是儿子B<slot></slot></div>`,
}
const Parents = {
methods: {
},
//实例化BBorder这个子组件
template: `<div>我是父组件:{{msg}}
<BBorder>无敌牛逼</BBorder>
</div>`,
//注册两个子组件
components: {
BBorder
}
}
//实例化一个vue,并且绑定到app这个元素
const vm = new Vue({
el: '#app',
components: {
Parents,
}
})
输出效果如图:
我们可以看到,我们添加的内容输出了。当组件渲染的时候, 将会被替换为我们添加的内容(在上面也就是“无敌牛逼”)。插槽内可以包含任何模板代码,包括 HTML,甚至别的组件。但是如果你的子组件中没有插槽,那么你添加的所有内容都会被抛弃。 所以要注意使用。
三、编译的作用域
我们要千万记住的是:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
我们可以在插槽中读取到当前父组件的数据,但是无法读取到子组件的。
<navigation-link url="/profile">
Logged in as {{ user.name }}
LOggedLocaTion in as {{ url }}
</navigation-link>
就比如上面的,我们可以读取到 user.name (父组件中data的值),但我们无法读取到 url ,因为这个值是传给子组件的。他们两者的作用域是不一样的。
四、后备内容
其实这个就像我们使用到的 父子组件传值中的 prop 属性的默认值,当我们的父组件没有传值的时候就会使用子组件中设置的默认值。
//子组件B
const BBorder = {
template: `<div>我是儿子B<slot>我是默认的插槽值</slot></div>`,
}
const Parents = {
methods: {
},
//实例化BBorder这个子组件
template: `<div>我是父组件:{{msg}}
<BBorder></BBorder>
</div>`,
//注册两个子组件
components: {
BBorder
}
}
//实例化一个vue,并且绑定到app这个元素
const vm = new Vue({
el: '#app',
components: {
Parents,
}
})
输出效果如图:
五、具名插槽
当我们有多个插槽的时候,我们就容易分不清内容的位置了,所以,为了解决这个问题,出现了具名插槽。
//子组件B
const BBorder = {
template: `<div>我是儿子B
<slot name= 'header'> 这里是头部</slot>
<slot> 这里是默认 </slot>
<slot name='footer'> 这里是尾部 </slot></div>`,
}
const Parents = {
methods: {
},
//实例化BBorder这个子组件
template: `<div>我是父组件:{{msg}}
<BBorder>
<template v-slot:header> 头部 </template>
<p>A 9999999999999</p>
<template v-slot:footer> 尾部 </template>
</BBorder>
</div>`,
//注册两个子组件
components: {
BBorder
}
}
//实例化一个vue,并且绑定到app这个元素
const vm = new Vue({
el: '#app',
components: {
Parents,
}
})
输出效果如下,我们可以实现根据我们的需求来指定位置和内容。注意的是,如果我们使用的是具名插槽,我们需要把内容放到 template 里面,不在该闭合里面的都会被当为默认的。同时我们的 template 需要在该元素上使用 v-slot 标签。(ps: v-slot 标签只能用在 template 中)
六、作用域插槽
由上面我们知道,我们插槽中只能读取到父组件的值,那么能不能让我们读取到子组件里面的值呢?
我们可以下面的操作:在子组件绑定值
<span>
<slot :user="user">
{{ user.lastName }}
</slot>
</span>
...
data() {
return{
user: {
firstName: 'AAA',
lastName: 'aaa',
}
}
}
然后再父组件中可以使用带值的 v-slot来接收我们子组件的值,在这里 default 为我们定义的插槽的名称,一一对应 或者为默认值。后面的名可以我们自定义来接收。
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
通过上面的设置,我们就可以接收到子组件的值了。
补充:
1.跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header:
2.也可以是动态的命名, 比如:v-slot:[dynamicSlotName]