Vue 使用slot分发内容
前面的话当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot,这个过程叫作内容分发(transclusion)。props传递数据、events触发事件和slot内容分发就构成了Vue组件的3个API来源,再复杂的组件也是由这3部分组成的。 本文将详细介绍内容分发slot。编译作用域正式介绍slot前,需要明确编译的作用域在哪。比如父组件的有如下模板:<chil...
前面的话
当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot,这个过程叫作内容分发(transclusion)。props传递数据、events触发事件和slot内容分发就构成了Vue组件的3个API来源,再复杂的组件也是由这3部分组成的。 本文将详细介绍内容分发slot。
编译作用域
正式介绍slot前,需要明确编译的作用域在哪。比如父组件的有如下模板:
<child-component>
{{ massage }}
</child-component>
这里的massage是一个slot,但是它绑定的是父组件的数据,而不是组件< child-conmponent>的数据 ,组件作用域的简单来说:父组件模板的内容是在父组件作用域内编译,子组件模板的内容是在子组件作用域内编译。
示例:
<div id="app1">
<child-component v-show="showChild"></child-component>
</div>
<script>
Vue.component('child-component',{
template:'<div>子组件</div>'
});
var app1 = new Vue({
el: '#app1',
data:{
showChild: true
}
})
</script>
这里的状态showChild绑定的是父组件的数据,如果想绑定子组件,应该是:
<div id="app2">
<child-component></child-component>
</div>
<script>
Vue.component('child-component',{
template:'<div v-show="showChild">子组件</div>',
data: function() {
return {
showChild: true
}
}
});
var app2 = new Vue({
el: '#app2'
})
</script>
所以类似的,slot分发的内容,作用域是在父组件作用域上的 。
默认丢弃
一般地,如果子组件模板不包含插口,父组件的内容将会被丢弃。
<div id="app3">
<parent-component></parent-component>
</div>
<script>
var childNode = {
template: `
<div class="child">我是子组件
<p>子组件内容</p>
</div>
`
}
var parentNode = {
template:`
<div class="parent">我是父组件
<child-component>
<p>测试内容</p>
</child-component>
</div>
`,
components: {
'child-component': childNode
}
}
var app3 = new Vue({
el: "#app3",
components: {
'parent-component': parentNode
}
});
</script>
这里父组件 < child-component>里的 < p>测试内容< /p>自动丢弃
内联模板
一般的如果子组件有inline-template特性,组件将把它的内容当作它的模板,而忽略真实的模板内容,但是inline-template让模板的作用域难以理解
<div id="app4">
<parent-component></parent-component>
</div>
<script>
var childNode = {
template: `
<div class="child">
<p>子组件</p>
</div>
`
}
var parentNode = {
template: `<div class="parent">
<child-component inline-template>
<p>测试内容</p>
</child-component>
</div>`,
components: {
"child-component": childNode
}
}
var app4 = new Vue({
el: '#app4',
components: {
"parent-component": parentNode
}
})
</script>
单个Slot
在子组件内使用特殊的元素就可以为这个子组件开启一个slot(插槽),当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身。
<div id="app5">
<parent-component></parent-component>
</div>
<script>
var childNode = {
template: `
<div class="child">
<slot></slot>
</div>
`
}
var parentNode = {
template:`<div class="parent">
<child-component>
<p>分发内容</p>
<p>测试内容</p>
</child-component>
</div>`,
components: {
"child-component":childNode
}
}
var app5 = new Vue({
el: '#app5',
components: {
'parent-component': parentNode
}
})
</script>
默认值
最初在标签中的任何内容都被视为备用内容,或者称为默认值。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才具有备用内容 。
当slot存在默认值,且没有要插入的内容时,显示默认值
<div id="app6">
<parent-component></parent-component>
</div>
<script>
var childNode = {
template: `
<div class="child">
<slot><p>默认值</p></slot>
</div>
`
}
var parentNode = {
template:`<div class="parent">
<child-component>
</child-component>
</div>`,
components: {
"child-component":childNode
}
}
var app6 = new Vue({
el: '#app6',
components: {
'parent-component': parentNode
}
})
</script>
当slot存在默认值时,且父元素在< child-component>中存在要插入的内容时,则显示插入的内容,不显示默认值。
具名Slot
给元素指定一个特殊的属性name来配置如何分发内容,具名Slot可以与单个Slot共存。具名 slot 将匹配内容片段中有对应 slot 特性的元素。
<div id="app7">
<parent-component></parent-component>
</div>
<script>
var childNode = {
template: `
<div class="child">
<slot name="header"><p>我是头部默认值</p></slot>
<slot name="body"><p>我是身体默认值</p></slot>
<slot name="footer"><p>我是尾部默认值</p></slot>
</div>
`
}
var parentNode = {
template:`<div class="parent">
<child-component>
<p slot="header">头部信息</p>
<p slot="footer">尾部信息</p>
</child-component>
</div>`,
components: {
"child-component":childNode
}
}
var app7 = new Vue({
el: '#app7',
components: {
'parent-component': parentNode
}
})
</script>
也可以有单个Slot,它将作为默认slot出现。
<div id="app8">
<parent-component></parent-component>
</div>
<script>
var childNode = {
template: `
<div class="child">
<slot name="header"><p>我是头部默认值</p></slot>
<slot></slot>
<slot name="footer"><p>我是尾部默认值</p></slot>
</div>
`
}
var parentNode = {
template:`<div class="parent">
<child-component>
<p slot="header">头部信息</p>
<p>我是其他内容</p>
<p slot="footer">尾部信息</p>
</child-component>
</div>`,
components: {
"child-component":childNode
}
}
var app8 = new Vue({
el: '#app8',
components: {
'parent-component': parentNode
}
})
</script>
作用域插槽
作用域插槽是一种特殊的slot,使用的一个可以复用的模板来代替已渲染元素。
在子组件中,只需将数据传递到插槽,就像将 props 传递给组件一样
<div class="child">
<slot msg="来自子组件的内容"></slot>
</div>
在父组件中,具有特殊属性scope的元素必须存在,表示作用域插槽的模板。scope的值是一个临时变量props,此变量接受从子组件中传递的对象msg
<div class="parent">
<child-component>
<template scope="props">
<p>{{props.msg}}</p>
</template>
</child-component>
</div>
示例:
<div id="app9">
<parent-component></parent-component>
</div>
<script>
var childNode = {
template: `
<div class="child">
<slot msg="来自子组件内容"></slot>
</div>
`
}
var parentNode = {
template:`<div class="parent">
<child-component>
<template scope="props">
<p>来自父组件的内容</p>
<p>{{props.msg}}</p>
</template>
</child-component>
</div>`,
components: {
"child-component": childNode
}
}
var app9 = new Vue({
el: '#app9',
components: {
'parent-component': parentNode
}
})
</script>
作用域插槽更具有代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项 。
<div id="app10">
<parent-component></parent-component>
</div>
<script>
var childNode = {
props:{
books:{
type: Array,
default: function(){
return [];
}
}
},
template: `
<div class="child" >
<ul>
<slot name="book" v-for="book in books" :book-name="book.name"></slot>
</ul>
</div>
`
}
var parentNode = {
template: `
<div class="parent">
<child-component :books="books">
<template slot="book" scope="props">
<li >{{props.bookName}}</li>
</template>
</child-component>
</div>
`,
components: {
'child-component': childNode
},
data(){
return {
books:[
{name: '《Vue.js实战》'},
{name: '《JavaScript语言精髓》'},
{name: '《JavaScript高级程序设计》'}
]
}
}
}
var app10 = new Vue({
el:"#app10",
components: {
'parent-component': parentNode
}
})
</script>
子组件child-component接收一个来自父级的prop数组books,并且将它在name为book的slot上使用v-for指令循环,同时暴露一个变量bookName。
此例的用意主要是介绍作用域插槽的用法,并没有加入使用场景,而作用域插槽的使用场景就是既可以复用子组件的slot,又可以使slot内容不一致。
访问slot
Vue.js 2.x提供了用来访问被slot分发的内容方法$slots。
<div id="app11">
<child-component>
<h2 slot="header">标题</h2>
<p>正文内容</p>
<div slot="footer">底部信息</div>
</child-component>
</div>
<script>
Vue.component('child-component',{
template: `
<div class="child">
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
`,
mounted: function () {
var header = this.$slots.header;
var main = this.$slots.default;
var footer = this.$slots.footer;
console.log(footer);
console.log(footer[0].elm.innerHTML);
}
});
var app11 = new Vue({
el:'#app11'
})
</script>
通过$ slots可以访问某个具名slot,this.$slot.default包括了所有没有被包含在具名slot中的节点。
更多推荐
所有评论(0)