组件的插槽
组件本身就是一个容器,也可以是一个vue对象,也是一个虚拟DOM
普通插槽
组件本身是一个容器,这个容器本身是空的,当我们把需要封装的html结构装进去之后,我们可以认为这个容器被塞满了,那就意味着,我们无法想组件内部添加新的html结构进去,但是我们想在每次调用组件的时候需要渲染一些个性化的东西,这个时候我们就需要给组件做一个预留空间,这个预留空间就是插槽
html
<body>
<div id="app">
<one>
<p>我是子标题</p>
</one>
</div>
<template id="temp1">
<div>
<h2>我是标题</h2>
</div>
</template>
</body>
<script src="js/vue.js"></script>
<script>
let one = {
template:"#temp1"
}
new Vue({
el:"#app",
components:{
one
}
})
</script>
代码分析:
以上的代码渲染之后,我们在虚拟DOM中写入了一个p标签,着就是所谓的插入,但是并没有效果,因为我们并没有在one组件中预留空间,也就是插槽
如果想在组件内部预留插槽,使用
<slot></slot>
html
<body>
<div id="app">
<one>
<p>我是子标题</p>
<p>我是子标题</p>
<p>我是子标题</p>
<p>我是子标题</p>
<p>我是子标题</p>
<p>我是子标题</p>
</one>
</div>
<template id="temp1">
<div>
<h2>我是标题</h2>
<slot>
<!-- 组件内预留给插槽的空间 -->
</slot>
</div>
</template>
</body>
<script src="js/vue.js"></script>
<script>
let one = {
template:"#temp1"
}
new Vue({
el:"#app",
components:{
one
}
})
</script>
一个插入点插入多个插槽
现在我们想让刚才的代码中前3个p标签出现在标题的上方,后面3个出现在标题的下方
html
<body>
<div id="app">
<one>
<p>我是子标题</p>
<p>我是子标题</p>
<p>我是子标题</p>
<p>我是子标题</p>
<p>我是子标题</p>
<p>我是子标题</p>
</one>
</div>
<template id="temp1">
<div>
<slot></slot>
<h2>我是标题</h2>
<slot></slot>
</div>
</template>
</body>
<script src="js/vue.js"></script>
<script>
let one = {
template:"#temp1"
}
new Vue({
el:"#app",
components:{
one
}
})
</script>
代码分析:
上面的代码中,我们发现,在模板中的标题上方也制作了一个插槽slot,但是,从效果上来看只是单纯把插入的内容复制了一份,分别插入不同的插槽里面
其实我们上面写的都是默认插槽,把插槽语法写完成如下
html
<div id="app">
<one>
<p slot="default">我是子标题</p>
<p slot="default">我是子标题</p>
<p slot="default">我是子标题</p>
<p slot="default">我是子标题</p>
<p slot="default">我是子标题</p>
<p slot="default">我是子标题</p>
</one>
</div>
<template id="temp1">
<div>
<slot name="default"></slot>
<h2>我是标题</h2>
<slot name="default"></slot>
</div>
</template>
默认情况下,所有的slot都必须要指定一个name,只是如果我们不自己指定的话,系统会给我们指定一个默认的名称default
具名插槽
在上面的插槽中,我们所有的slot如果在不指定name的情况下全都是default,但是这个name属性可以设置的,如果设置一些其他的值,我们就把这个插槽叫做具名插槽
html
<div id="app">
<one>
<p slot="top">我是子标题</p>
<p slot="top">我是子标题</p>
<p slot="top">我是子标题</p>
<p slot="bottom">我是子标题</p>
<p slot="bottom">我是子标题</p>
<p slot="bottom">我是子标题</p>
</one>
</div>
<template id="temp1">
<div>
<slot name="top"></slot>
<h2>我是标题</h2>
<slot name="bottom"></slot>
</div>
</template>
代码分析:
在上面的代码中,我们可以为插槽取一个名字,从而实现一对多,或者多对一的插入,当插槽有了名字之后,我们就可以在插入的时候指定插入到某一个插槽中
- 我们组件里面定义插槽的时候使用slot标签,并且这个标签上面定义name属性,成为具名插槽
- 在调用组件的时候,我们可以向指定的插槽插入内容,只需要在这个插入的元素上面添加一个slot="插槽名"即可
- 具名插槽是可以多次使用的,所以我们可以把上面的top复制一份放到bottom的下面,这样上面三个p标签也会出现在下面的插槽里面
插槽作用域
html
<body>
<div id="app">
<one>
<p>我想要拿到组件内部的userName</p>
</one>
</div>
<template id="temp1">
<div>
<h2>我是一个组件</h2>
<slot>
<!-- 组件的预留空间 -->
</slot>
</div>
</template>
</body>
<script src="js/vue.js"></script>
<script>
let one = {
template:"#temp1",
data(){
return {
userName:"zhangsan"
}
}
}
new Vue({
el:"#app",
components:{
one
}
})
</script>
之前我们一直都是外部的数据传递给内部,如果要把组件内部的数据传递给外部,我们可以:
1、利用对象的堆栈原理
2、自定义事件
注意:
我们现在要做一个区分,我们是可以把数据渲染在组件里面的,也可以渲染在插槽里面,但是这两种方式在向组件外部传值的时候是有区别的,我们上面说的这两种方法,只针对在组件内渲染的,如果是渲染在组件的插槽内部的,我们可以通过插槽作用域取拿
比如我们现在要userName的值传递到外面,我们可以在插入的标签上面添加一个slot-scope="scope"
html
<body>
<div id="app">
<one>
<div slot-scope="scope">
<p>{{scope.userName}}</p>
<p>{{scope.age}}</p>
</div>
</one>
</div>
<template id="temp1">
<div>
<h2>我是一个组件</h2>
<slot :user-name="userName" :age="18">
<!-- 组件的预留空间 -->
</slot>
</div>
</template>
</body>
<script src="js/vue.js"></script>
<script>
let one = {
template:"#temp1",
data(){
return {
userName:"zhangsan"
}
}
}
new Vue({
el:"#app",
components:{
one
}
})
</script>
插槽作用域旧版本语法
html
<div id="app">
<one>
<div slot="footer" slot-scope="scope">
<p>{{scope.userName}}</p>
<p>{{scope.age}}</p>
</div>
</one>
</div>
<template id="temp1">
<div>
<h2>我是一个组件</h2>
<slot name="footer" :user-name="userName" :age="18">
<!-- 组件的预留空间 -->
</slot>
</div>
</template>
新版本语法
html
<one>
<template v-slot:footer="scope">
<p>{{scope.userName}}</p>
<p>{{scope.age}}</p>
</template>
</one>
最新语法
html
<one>
<template #footer="{userName,age}">
<p>{{userName}}</p>"
<p>{{age}}</p>
</template>
</one>
代码分析:
最新语法可以直接解构获取,并且v-slot这个指令也直接使用#来替代,简化代码