Vue slot插槽
文章目录
一.slot简介
slot(插槽),我们理解为就是"占位",还有些人可能没那么儒雅,称之为"占坑,茅坑",所以有坑,我们就要填.真正的作用是:在组件模版中占一个坑,当我们使用这个组件标签的时候,通过替换组件模版中slot位置,来填这个坑,并且可以作为承载分发的内容出口,
slot可以干哪些活呢? 如果我们需要父组件在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、怎么显示,就可以使用slot分发
二.slot基础使用
我们知道,在调用组件时,没有使用插槽的情况下,写在组件标签内部的内容是默认不会被渲染的.但是呢,有时候我们需要在组件标签内的内容能够被渲染出来
举个例子:我们做一个弹出框组件,需要做到外部结构固定,弹框内部的结构可以通过不同的场景变化,比如某些地方是否显示关闭按钮,是否显示icon图标,或者弹框中间内容的变化等等.要通过子组件自己封装,肯定是不行的,这个时候就可以使用slot占位,传递dom结构来解决
1.slot内容(插槽内容)
slot内容、插槽内容、插槽模板内容 :调用组件时写在组件标签内的内容就叫做 slot 内容
<div id="app">
<!-- slot内容、插槽内容、插槽模板内容 -->
<hello-slot>默认不被喧嚷</hello-slot>
<!-- 调用组件时写在组件标签内的内容就叫做 slot 内容 -->
<!-- 标签内的内容会替换组件模版内容中的slot标签 -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('hello-slot',{
template:`
<div>
<h1>了解插槽</h1>
<slot></slot>
</div>
`,
});
const vm=new Vue({
el:'#app'
});
</script>
标签内的内容会替换组件模版内容中的slot标签
2.slot后备内容(默认内容)
有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
通俗点说,就是你设置了slot的后备内容,如果调用组件时,你在标签组件
<div id="app">
<hello-slot>第一个组件,有内容,会替换slot后备内容</hello-slot>
<!-- 第二个组件,标签中没有内容,会默认加载slot的后备内容 -->
<hello-slot></hello-slot>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('hello-slot',{
template:`
<div>
<h1>slot后备内容</h1>
<slot>
<p>我是slot的后备内容</p>
</slot>
</div>
`,
});
const vm=new Vue({
el:'#app'
});
</script>
说个案例,比如我们在弹框中,我们一直要保持弹框是默认的按钮文字内容是"保存",但是呢产品经理说要把保存改成"确定",而其他应用此组件的不改,我们这里就可以使用slot的后备内容,默认为文字是"保存",想要改成"确定"时,直接在组件标签中写"确定",就可以实现了
3.slot 具名(具名插槽)
给slot标签设置一个name属性,这个时候slot标签就叫做具名插槽
通俗点说就是:给这个坑起个名字,那么这个坑就叫做有名字的坑
有时候,我们需要使用多个插槽,也就意味着slot在一个组件中,是可以使用多次的
给slot取好名字之后,slot内容想要渲染在哪个slot里面,就需要设置slot属性,属性的值为某个slot的名字,另外需要注意的是:slot有个默认的名字叫做"default"
<!-- 二者全等于 -->
<slot></slot> === <slot name="default"></slot>
下面这种方式在vue 2.6.0版本已经废弃了,但是还是可以使用,也就是通过使用slot属性的形式
<div id="app">
<hello-slot>
<!-- 使用slot属性 -->
<p slot="hello">名字为hello的插槽</p>
<p slot="world">名字为world的插槽</p>
<p>默认default的插槽</p>
</hello-slot>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('hello-slot', {
template: `
<div>
<h1>slot具名</h1>
<slot name="hello"></slot>
<slot name="world"></slot>
<slot name="default"></slot>
<slot></slot>
</div>
`,
});
const vm = new Vue({
el: '#app'
});
</script>
下面是2.6.0之后的具名插槽使用用法
<div id="app">
<hello-slot>
<!-- 注意:v-slot指令只能作用在template标签上(除了一种例外:
当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用) -->
<template v-slot:hello>
<p>名字为hello的插槽</p>
</template>
<!-- v-slot的简写是 # -->
<template #world>
<p>名字为world的插槽</p>
</template>
<!-- <template #default>
<p>名字为default的插槽</p>
</template> -->
<!-- 如果没有被带有v-slot指令的template包裹,则会被当做slot的默认内容,当template没有使用 默认名字的插槽时会被喧嚷 -->
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<!-- 另外:template使用插槽时,不能同时使用多次,会覆盖掉之前的template -->
<template>
<p>如果没有被template包裹</p>
</template>
</hello-slot>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('hello-slot', {
template: `
<div>
<h1>slot具名</h1>
<slot name="hello"></slot>
<slot name="world"></slot>
<slot name="default"></slot>
<slot></slot>
</div>
`,
});
const vm = new Vue({
el: '#app'
});
</script>
总结几点:
-
注意:v-slot指令只能作用在template标签上(除了一种例外:当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用
-
v-slot的简写是 #
```js <template #world> <p>名字为world的插槽</p> </template> <!-- 全等于 --> <template v-slot:world> <p>名字为world的插槽</p> </template> ```
-
如果没有被带有v-slot指令的template包裹,则会被当做slot的默认内容,当template没有使用 默认名字的插槽时会被喧嚷 注意:必须要带v-slot指令,只有template包裹也不行!!!还是会被当做默认内容
-
template使用插槽时,不能同时使用多次,会覆盖掉之前的template
<template #world> <p>名字为world的插槽</p> </template> <!-- 另外:template使用插槽时,不能同时使用多次,会覆盖掉之前的template --> <template #world> <p>名字为world的插槽 此处会覆盖上面的内容</p> </template>
-
slot没有设置name属性时,默认还是default
<template #default> <p>名字为default的插槽</p> </template>
4.slot编译作用域
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
<div id="app">
// app节点里包含的内容都是父级作用域
<hello-slot>
<p>{{msg}}</p>
</hello-slot>
<hello-slot>
<template #hello>
<p>{{msg}}</p>
</template>
</hello-slot>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('hello-slot', {
//子组件中的template内容都是子作用域
template: `
<div>
<h1>slot编译作用域</h1>
<slot name="hello"></slot>
<slot>
<p>{{msg}}</p>
</slot>
</div>
`,
data() {
return {
msg: 'hello'
}
}
});
const vm = new Vue({
el: '#app',
data: {
msg: "root"
}
});
</script>
上述代码,都会打印一个msg,但是由于slot编译作用域的关系,打印的msg属于父组件还是子组件,是值得考虑的,上面说了,父级编译父级的,子级编译子级的
总结一句话就是:虽然说插槽内容最终渲染在子组件内,但是它的编译作用域还是要看插槽内容写在哪个组件的template里面.而不是看插槽内容最终渲染在哪个组件的template里面的slot标签
注意理解:写在组件标签内的插槽内容是不属于组件的template的,只是通过slot可以让他渲染在组件里面去.
其实这么看就行了:app节点里包含的内容都是父级作用域,而子组件中的template选项内容都是子作用域
5.作用域插槽
作用域插槽的概念是:希望父组件的插槽内容使用子组件的作用域
<div id="app">
<hello>
// xxxx 如何能够用上 hello 组件中 的 数据
<p>我是一个插槽内容。{{xxxx}} </p>
</hello>
</div>
因为slot编译作用域的缘故,父组件的插槽内容不能直接获取子组件作用域,所以这里需要使用作用域插槽
使用方式如下:
-
定义slot的时候,将要在插槽内容中使用的数据,绑定在slot标签上,注意:不能绑定name属性,因为name属性有特殊作用,name属性用来具名插槽
<slot :age="age" :sex="sex" ></slot>
-
在插槽内容的标签上,设置slot-scope尚属性,属性值自定义,其实就是在slot标签绑定的属性上,封装成了一个大对象
<!-- obj==={age:'',sex=''} --> <p slot-scope="obj">{{age}} {{sex}}</p>
-
解构赋值的用法
<!-- 还有一种方式,解构赋值 --> <p slot-scope={age,sex}>{{ age }} {{ sex }}</p>
来个示例:
<div id="app">
<hello-slot>
<!--
使用slot-scope属性,值是一个对象,对象里包含子组件中slot传过来的值
obj === { a: "hello", b: 19 }
-->
<!-- <p slot-scope="obj">{{ obj.msg }} {{ obj.age }}</p> -->
<!-- 还有一种方式,解构赋值 -->
<p slot-scope={msg,age}>{{ msg }} {{ age }}</p>
</hello-slot>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('hello-slot', {
template: `
<div>
<h1>slot作用域插槽</h1>
<slot :msg="msg" :age="age"></slot>
</div>
`,
data() {
return {
msg: 'hello',
age: 19,
}
}
});
const vm = new Vue({
el: '#app',
});
</script>
还需要注意:使用slot-scope属性也是在2.6.0之后废弃了的,但是很多公司还是用的之前版本,下面是2.6.0之后的新语法
<div id="app">
<hello-slot>
<!-- 没有名字,默认default -->
<template #default="obj">
<p>{{obj.msg}}</p>
</template>
<!-- 具名插槽 -->
<template #hello="hello">
<p>{{hello.msg}}</p>
</template>
</hello-slot>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('hello-slot', {
template: `
<div>
<h1>slot作用域插槽</h1>
<slot name='hello' :msg="msg"></slot>
<slot :msg="msg"></slot>
</div>
`,
data() {
return {
msg: 'hello',
}
}
});
const vm = new Vue({
el: '#app',
});
</script>
6.扩展新语法
-
slot 插槽 在 2.6.0 这个版本的时候,做了更新。提供了一个新的指令叫做 v-slot。后续实现具名插槽与作用域插槽都使用 v-slot 来实现
-
2.6.0 版本在什么时候发布的:2019-02-04号(农历2018年过年那天)
-
v-slot语法
v-slot:xxxx=yyyy - xxxx 插槽名字 - yyyy 作用域插槽数据 如下: <template #hello="hello"> <p>{{hello.msg}}</p> </template>
1.v-slot 必须用在 template 元素上。也就是说 插槽内容 需要通过 template 元素进行包裹
2.如果插槽没有设置 name 名字那v-slot === v-slot:default
3.v-bind 指令有个简写 :
v-on 指令有个简写 @
v-slot 指令有个简写 # (注意,使用 简写时必须携带名字。 默认的名字需要写成 #default)4.插槽只有一个的情况下,可以不使用 template 去包裹插槽内容。而是直接将 v-slot 写在组件标签上。
//子组件传过来的,可以v-slot 写在组件标签上,解构赋值 <hello-slot v-slot="{user}"> {{user.name}} </hello-slot> Vue.component('hello-slot', { template: ` <div> <slot :user='user'></slot> </div> `, data() { return { user: { name: 'liuqiao' } } } });