在开始讲插槽之前,我们来思考一下:我们都知道,父组件可以随意地向子组件传递一个值,那么问题来了,如果我们要传递一个dom元素,比如说p标签呢?
这个时候,我们会发现,p标签被转义了,没有直接渲染出来:(
为了p标签的正常渲染,可以使用v-html,但是与此同时,它外层又会多了一个div标签:
聪明的童鞋可能想到——用模板占位符template啊!其实在这里它么得用,是渲染不出来的:
害,这也不行,那也不行,那究竟肿么搞嘞??!
当当当当~主角登场——插槽!
1.插槽是什么?
插槽slot,可以通俗地理解成占位符,在组件模板中占好了位置,当使用该组件标签时候,原组件标签的内容(可以是任意的html代码或者其他的组件,比如图标)就会自动替换掉组件模板中的slot。
插槽就是一个萝卜一个坑,子组件挖坑(slot插槽),父组件填萝卜(内容);具名插槽就是把萝卜和坑标了名称,只能对号入座,最后多的无名萝卜进无名的坑(默认插槽),没有无名的坑就丢弃萝卜,坑里有本来就有萝卜那就把萝卜拔出来填父组件的萝卜。
父组件在调用子组件的时候,插入了一个p标签,也就是插槽之中的内容;子组件直接在模板里用slot标签就可以用到父组件传来的p标签了:
通过插槽,可以更方便父组件向子组件传递dom元素,子组件使用的时候,通过slot标签进行引用即可。
2.插槽的作用域
插槽跟模板其他地方一样都可以访问相同的实例属性(也就是相同的"作用域"),而不能访问的作用域
<test>
//插槽可以获取到组件里的内容
Hello {{word}}
</test>
data(){
return{
word:'world!'
}
}
//这里是获取不到name的,因为其这个值是
传给<test> 的,而不是在组件*内部*定义的。
<test name='you'>
Hello {{name}}
</test>
3.后备内容(默认内容)插槽
有时候我们需要给插槽设置一个具体的默认内容,当别的组件没有给你内容的时候,那么默认的内容就会被渲染
//在slot插槽里设置默认内容 Submit
<button>
<slot>Submit</slot>
</button>
//不提供内容时使用:
<test></test>
//那么最后设置的默认内容 Submit 将会被渲染为:
<button>
Submit
</button>
//提供内容时使用:
<test>按钮</test>
//那么这个提供的内容将会替代默认的内容被渲染出来:
<button>
按钮
</button>
4.具名插槽
可以给每个dom标签起名,在子组件里通过“具名插槽”使用不同的插槽内容。有时候我们一个组件里需要多个插槽
对于这样的情况,元素有一个特殊的特性:name ,这个特性可以用来定义额外的插槽
如果一个插槽不带name属性的话,那么它的name默认为default
在向具名插槽提供内容的时候,建议在元素上使用v-slot指令,并以参数的形式提供其名称
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
具名插槽的缩写
2.6.0 新增,与 v-bind v-on 一样,v-slot 使用 # 进行缩写,例如 v-slot:header 可以被重写为 #header 。并且,与前两者一样,指令缩写后面必须接参数,否则无效。
5.作用域插槽
当子组件做循环或者某一部分dom结构需要由外部传递的时候,要用到作用域插槽。
举个栗子:
子组件要实现一个功能——去循环显示一个列表:
(1)那么在子组件里面我定义一组数据,也就是数组list:[1,2,3,4]。
(2)在这个模板里显示一个列表,所以我会写一个ul。然后呢里面去写一个li。循环显示着里标签v-for=“item of list”。
(3)li部分显示item就可以了。
组件有可能在很多的地方被调用,那么接下来我有一个需求:
我希望在不同的地方都有child组件的时候,这个列表到底怎么循环,列表的样式不是我这个child组件所控制的,而是父组件告诉子组件:每一项应该如何渲染。
也就是我们的代码把这个li标签要给它去掉,取而代之我们写一个slot。
父组件往子组件传递一个slot,告诉子组件该怎么显示里边的每项:
(1)首先一定要在最外层套一个template,这是一个固定的写法。
(2)同时,你要写一个slot-scope=“props”。这个属性值可以自己随便定义一个。
他的意思是什么呢?当子组件用slot的时候会往slot里面传递一个item数据,那么我们用child的时候就可以用这个item的数据,item就放在你写的slot-scope后面跟的这个属性之中。显示内容通过差值表达式的形式直接写{{props.item}}就行了。
从头缕一缕思路:
父组件调用子组件时传递了一个(作用域)插槽:
- 作用域插槽必须是template标签包裹的内容
- 同时,插槽要声明从子组件接收的数据要放在哪?也就是 slot-scope声明下的“props”里(名称可自定义)
- 接下来,需要告诉子组件介绍的模板的信息(也就是如何展示) 例如:
<h1>{{props.item}}</h1>
子组件可以向插槽里面传数据,父组件传如果想接收这个数据,就必须在外层使用一个template,同时通过slot-scope对应的这个属性的名字来接收你传递过来的所有的数据,那这边我传了一个item过来,在父组件的这个作用域插槽里面,我就可以接收到这个item,那我再来使用它就不会有任何的问题啦。