Vue插槽的认识和基本使用

文章目录

  • 1.编译作用域
  • 2.插槽的基本使用
    • 2.1 插入基本的文本内容
    • 2.2 插入DOM标签
  • 3.插槽的默认内容
  • 4.具名插槽
    • 4.1 不使用具名插槽的问题
    • 4.2 使用具名插槽
  • 5.作用域插槽
    • 5.1 作用域插槽的理解和基本使用
    • 5.2 作用域插槽也可以解构使用
  • 6.v-slot 命令
    • 6.1 使用v-slot解决具名插槽问题
    • 6.2 使用v-slot处理作用域插槽问题
    • 6.3 作用域插槽的特殊使用
  • 7.动态插槽
  • 8.具名插槽的缩写

写在前面:
加入我们的需求是设置一个通用性的按钮,按钮的内容是使用的时候来决定,我们可能会通过一个属性来设置按钮的文本,示例代码如下:

<div id="app">
        <my-com value="注册"></my-com>
        <my-com value="提交"></my-com>
        <my-com value="重置"></my-com>
    </div>

    <template id="mycom">
        <button>{
     {
     value}}</button>
    </template>
    <script>
        let MyCom = ({
     
            props:["value"],
            template:"#mycom",
        })

        let vm = new Vue({
     
            el:"#app",
            components:{
     
                MyCom,
            }
        })
    </script>

此时我们在调用子组件的时候,设置不同的 value 值就会有不同的按钮.
在页面中的显示结果如下图所示:
Vue插槽的认识和基本使用_第1张图片
上面的方法虽然也可以满足我们的需求,但其实通过我们学习html ,我们其实更习惯下面的这种写法:

    <my-com>注册</my-com>
    <my-com>提交</my-com>
    <my-com>重置</my-com>

想要这么写,我们就必须使用到 slot 插槽.在此之前,我们先来看一下编译作用域

1.编译作用域

通过前面的学习,其实我们已经了解到父子组件都有自己不同的模板和独立的作用域,每一个组件在实例化的时候作用域都是独立的.

那么我们动一动我们可爱的小脑瓜,就会想到假如我们在父组件中使用子组件时,子组件中嵌套的内容会在哪个作用域里编译呢??

那我们就来看看下面的例子:

 <div id="app">
        <my-com>
            {
     {
     msg}}
        </my-com>
    </div>

    <template id="mycom">
        <button>{
     {
     msg}}</button>
    </template>
    <script>
        let MyCom = ({
     
            data(){
     
                return{
     
                    msg:"hello world"
                }
            },
            template:"#mycom",
        })

        let vm = new Vue({
     
            el:"#app",
            data:{
     
                msg:"你好,世界"
            },
            components:{
     
                MyCom,
            }
        })
    </script>

此时组件中嵌套的 msg 究竟是在父组件中的作用域中编译还是在子组件作用域编译呢?这涉及到 msg 使用的是父组件的数据还是子组件的数据.

答案当然就是父组件中的数据了,但是现在我们还没有办法演示这段代码的结果,等后面我们介绍完插槽,再来看看这里的结果会是怎样.

组件作用域简单的来说就是:

  1. 父组件模板的内容在父组件中编译
  2. 子组件模板中的内容在子组件中编译

所以就会有一个比较常见的错误,父组件模板内使用内将一个指令绑定到子组件内部的属性/方法
示例代码如下:

   <div id="app">
        <my-com v-show = "isShow"></my-com>
    </div>

    <template id="mycom">
        <button>按钮</button>
    </template>
    <script>
        let MyCom = ({
     
            data(){
     
                return{
     
                   isShow:true,
                }
            },
            template:"#mycom",
        })

        let vm = new Vue({
     
            el:"#app",
            components:{
     
                MyCom,
            }
        })
    </script>

报错信息如下图:
在这里插入图片描述
这样做会报错的原因是: isShow 是我们定义在子组件中的数据,父组件中并不能直接找到子组件的数据.

如果需要绑定到子组件作用域内的数据,就可以将指令绑定在子组件的根节点上.
如下:

<div id="app">
        <my-com></my-com>
    </div>
    <template id="mycom">
        <button  v-show = "isShow">按钮</button>
    </template>
    <script>
        let MyCom = ({
     
            data(){
     
                return{
     
                   isShow:false,
                }
            },
            template:"#mycom",
        })

        let vm = new Vue({
     
            el:"#app",
            components:{
     
                MyCom,
            }
        })
    </script>

这样做就不会报错.

自己可以慢慢体会一下这两种写法,通过上面的例子,我们就可以对 编译作用域 有一点点的了解.

那么想想我们开篇提出的问题,在父组件模板中使用子组件时,子组件中嵌套的内容是属于父组件的编译,但是我们希望这个内容能在子组件中使用,此时我们就需要一种方式来混合父组件的内容与子组件自己的模板,这个过程被称为内容分发.

2.插槽的基本使用

原来我们在组件里面是不能写内容的,因为写了之后也会被模板整体替换,现在有了插槽之后,我们就可以在组件里定义内容了.

2.1 插入基本的文本内容

还是上面的例子,我们子组件中嵌套的文本在子组件模板中使用,我们就需要在子组件模板中使用插槽

 <div id="app">
        <my-com>按钮</my-com>
        <my-com>{
     {
     value}}</my-com>
    </div>
    <template id="mycom">
        <button >
            <slot></slot>
        </button>
    </template>
    <script>
        let MyCom = ({
     
            template:"#mycom",
        })

        let vm = new Vue({
     
            el:"#app",
            data:{
     
                value:"注册"
            },
            components:{
     
                MyCom,
            }
        })
    </script>

使用插槽之后的结果:
Vue插槽的认识和基本使用_第2张图片
通过这种方法,我们就可以很方便的在父组件中,通过slot插槽向子组件中分发内容
那么我们想想,既然可以插入文本内容,那是不是也可以在里面插入 DOM 标签呢?

2.2 插入DOM标签

示例代码如下:

 <div id="app">
        <my-com>
            <h3>你好啊!我是依古比古</h3>
        </my-com>
        <my-com>
            <h2>你好,我是玛卡巴卡</h2>
        </my-com>
    </div>
    <template id="mycom">
       <div>
           <slot></slot>
       </div>
    </template>
    <script>
        let MyCom = ({
     
            template:"#mycom",
        })

        let vm = new Vue({
     
            el:"#app",
            components:{
     
                MyCom,
            }
        })
    </script>

结果如下图:
Vue插槽的认识和基本使用_第3张图片
通过上面的这段代码,我们发现插槽不仅仅可以插入文本内容,还可以插入 DOM 标签.
插槽的更多作用我们往下面继续看.

3.插槽的默认内容

我们并不是每一次使用子组件的时候都会给里面插入内容,如果我们不给里面插入内容,那么里面将会是什么也没有,所以我们需要设置默认内容.

如果我们需要在使用子组件时未插入内容时. 显示一段默认的内容,我们就可以将默认的内容嵌套在slot 标签中

这就是插槽的备用内容, 备用内容在子组件的作用域内编译. 并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。

还是使用按钮的那个例子,看下面的代码:

 <div id="app">
        <my-com></my-com>
        <my-com>
            {
     {
     value}}
        </my-com>
    </div>
    <template id="mycom">
        <button>
            <slot>我是默认内容</slot>
        </button>
    </template>
    <script>
        let MyCom = ({
     
            template:"#mycom",
        })

        let vm = new Vue({
     
            el:"#app",
            data:{
     
                value:"注册"
            },
            components:{
     
                MyCom,
            }
        })
    </script>

显示的结果如下图:
Vue插槽的认识和基本使用_第4张图片
这样做就算子组件没有给我们分发内容,我们也可以使用默认内容,不至于让显示的内容为空.

4.具名插槽

4.1 不使用具名插槽的问题

在我们使用组件的时候,有时候需要在不同的位置插入不同的内容,只有一个插槽很可能不能满足我们的需求,看示例:

我们的需求是:

  1. 子组件是一篇文章
  2. 父组件在调用子组件的时候需要给子组件插入标题,正文,时间等信息.

示例代码如下:

 <div id="app">
        <my-com>
            <h2>标题~~标题</h2>
            <p>希望第二天的我</p>
            <p>仍然对不同充满宽容</p>
            <p>继续对未知饱含敬畏</p>
            <span>2020612</span>
        </my-com>

    </div>
    <template id="mycom">
        <div class = "article">
            <div class="title">
                <slot></slot>
            </div>
            <div class="content">
                <slot></slot>
            </div>
            <div class="time">
                <slot></slot>
            </div>
        </div>
    </template>
    <script>
        let MyCom = ({
     
            template:"#mycom",
        })

        let vm = new Vue({
     
            el:"#app",
            data:{
     
                value:"注册"
            },
            components:{
     
                MyCom,
            }
        })
    </script>

Vue插槽的认识和基本使用_第5张图片
这个时候我们可以看到,我们要插入的内容被重复插入了三次,此时每一个插槽都插入了所有的内容,不符合我们的预期.

那么我们要怎样才能将分发的内容制定到具体的插槽上呢???

这个时候就需要具名插槽

4.2 使用具名插槽

元素有一个特殊的 name 属性, 使用 name 来进一步配置如何分发内容,每个插槽都可以有具体的名字.具名插槽将匹配内容片段中有对应的 slot 元素.

没有使用 name 属性的 slot 插槽被称为 匿名插槽,也可以称之为 默认插槽,我们在子组件中仍然可以有一个额匿名插槽,作为找不到匹配内容片段的备用插槽,若果没有默认插槽,这些找不到匹配的内容片段就会被抛弃.

看下面的示例代码:

   <div id="app">
        <my-com>
            <h2 slot = "title">标题~~标题</h2>
            <p>希望第二天的我</p>
            <p>仍然对不同充满宽容</p>
            <p>继续对未知饱含敬畏</p>
            <span slot = "time">2020612</span>
        </my-com>

    </div>
    <template id="mycom">
        <div class = "article">
            <div class="title">
                <slot name = "title"></slot>
            </div>
            <div class="content">
                <slot >这里是默认内容</slot>
            </div>
            <div class="time">
                <slot name = "time"></slot>
            </div>
        </div>
    </template>
    <script>
        let MyCom = ({
     
            template:"#mycom",
        })

        let vm = new Vue({
     
            el:"#app",
            components:{
     
                MyCom,
            }
        })
    </script>

Vue插槽的认识和基本使用_第6张图片
可以看到,完全就是我们想要的效果.分发的内容都正确的插到了插槽上面.

过上面的例子我们就知道了,slot 如果没有显示的使用 name 属性指定插槽的名字,那么slot默认有个名字default,默认插槽,如果在分发内容时,没有指定插槽,所有的内容都将默认插到默认插槽上.

5.作用域插槽

5.1 作用域插槽的理解和基本使用

通过前面的学习,我们已经知道,插槽的内容是最后在子组件模板上渲染的,那么就会在有些时候在分发内容中使用子组件中才会有的数据,这样的话我们该怎么办呢???

这个时候就要用到作用域插槽了.

作用域插槽是一种特殊类型的插槽,用作一个(能被传递数据的)可重用模板来代替已经渲染好的元素.

简而言之,就是利用slot 标签将子组件的数据传递到分发内中上,就像prop传递数据给组件一样

在父级中,具有特殊特性 slot-scope