前面的话

当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到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中的节点。

Logo

前往低代码交流专区

更多推荐