VUE的插槽(slot和slot-scope)

转载自https://blog.csdn.net/qq_33616027/article/details/90477960## VUE的插槽(slot和slot-scope)

前言

在之前的获取当前行数据中,最终的方案是使用了slot-scope="scope"的方式获取了当前行的数据,当时对slot-scope是个什么东东不了解。这两天查看了好多资料,有点明白了这个插槽是个什么东西。但是理解的不深,可能有错,先记下来,就当是整理一下思路。

什么是插槽

Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将<slot>元素作为承载分发内容的出口。

这是官方文档对插槽的介绍,看了介绍后,还是忍不住想问:这TM是什么玩意儿。结合官方文档中对插槽的介绍和demo,我自己有了一点理解,不过在说我的理解之前,还需要先说说几个概念。
首先就是组件。在VUE中,一个.vue文件就是一个组件,一个标签也是一个组件。不同之处就在于:.vue文件是自己写的组件,用Java做个对照,就如同你写的一个.java文件,比如叫个Student.java或者Teacher.java;标签是官方定义的组件,在Java中,就好像是String、Integer、Character这样的存在。
然后就是父组件和子组件。这个就简单了:A组件里面引用了B组件,那么A就是父组件,B就是子组件。
再接着说插槽。在我的理解中,插槽就是要将父组件中的内容渲染到子组件中。就好像是在子组件中留了一个空的位置(就像小霸王上的插卡口),然后把父组件中的内容插进去(你的游戏卡盘)。
A.vue把B.vue注册成为新的组件当做标签用,并在B标签中写了一些内容,然后在B.vue中用<slot></slot>把A.vue中B标签里的内容渲染出来。

插槽详解

准备工作

首先我准备了两个组件:defaultSlot.vue和childSlot.vue。
defaultSlot.vue中注册了childSlot.vue:

<script>
    import ChildSlot from '../../views/slot/childSlot'
    export default {
        name: "defaultSlot",
        components: {
            ChildSlot
        }
    }
</script>

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在官方文档中,插槽有三种:单个插槽、具名插槽、作用域插槽。

单个插槽

光从现象上来看,单个插槽(或者叫默认插槽,或者叫匿名插槽)和具名插槽是比较好理解的。
我已经在defaultSlot.vue中新注册了一个childSlot组件,现在我要在这个标签中写一点东西:

defaultSlot.vue中的代码

<template>
  <div style="width: 600px; text-align: left;">
      <div style="background: peru">
         这段话也是来自defaultSlot.vue,但它写在<code>ChildSlot</code>标签上面,因此,虽然他会有一个棕色的背景色,但不会被红色边框圈起来。
      </div>
      <ChildSlot>
          <div style="background: aquamarine">
              这是插槽的练习。这段话来自defaultSlot.vue,且会有一个浅绿色的背景色。本页面引入了childSlot.vue;而childSlot.vue中
              有一个<code>slot</code>标签,因此,在这段话的上面,还会显示childSlot.vue中的内容。而如果注释掉childSlot.vue
              中的<code>slot</code>标签,则本段内容不显示。因为这段话写在<code>ChildSlot</code>标签中,因此,它也会被一个红色
              的边框圈起来。
          </div>
      </ChildSlot>
      <div style="background: gray">
          这段话也是来自defaultSlot.vue,但它写在<code>ChildSlot</code>标签下面,因此,虽然他会有一个灰色的背景色,但不会被红色边框圈起来。
      </div>
  </div>
</template>
<script>
  import ChildSlot from '../../views/slot/childSlot'
   export default {
      name: "defaultSlot",
      components: {
          ChildSlot
      }
  }
</script>

    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

在childSlot.vue中,添加了一个插槽slot

childSlot.vue中的代码

<template>
  <div style="border: 2px solid red;">
     这是childSlot.vue中的内容,他会被一个红色的边框圈起来<br/>
  </div>
</template>
<script>
  export default {
      name: "childSlot"
  }
</script>

    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

那么在浏览器中的渲染结果是这样的:
在这里插入图片描述
可以发现,defaultSlot.vue中ChildSlot标签中的内容并没有被渲染出来。为什么呢?很简单,childSlot.vue中还没有使用插槽。现在我们就添加一个插槽:

<template>
    <div style="border: 2px solid red;">
        这是childSlot.vue中的内容,他会被一个红色的边框圈起来<br/>
        <slot></slot>
    </div>
</template>

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后再看页面:
在这里插入图片描述
defaultSlot.vue中ChildSlot标签中的内容被渲染了。

让我们来总结一下:
defaultSlot.vue中注册了新的标签ChildSlot,并在该标签中写入了内容。而在ChildSlot标签的模板文件childSlot.vue中,使用了<slot></slote>标签作为插槽,来渲染加载ChildSlot标签中的内容。
而上面这个<slot></slote>标签,就叫做单个插槽。

具名插槽

具名插槽,就是对slot标签设置name属性,从而来渲染指定的内容。
怎么玩呢?首先,要对defaultSlot.vue中ChildSlot里的内容进行分块,每一个块都用一个dom装载;然后,给这些装载了内容的dom设置一个slot属性;最后,在childSlot.vue中,给slot标签设置name属性,其属性值对应defaultSlot.vue中dom的slot属性值,这样,就会在这个slot标签中渲染指定的那个dom块中的内容。如果defaultSlot.vue中没有对dom设置slot属性,则默认为slot=“default”,在childSlot.vue中,会被渲染在没有name属性的slot标签的位置上。换句话说,下面这两行代码是等价的:

<slot></slot>
<slot name="default"></slot>

   
   
  • 1
  • 2

具名插槽的示例代码如下:

defaultSlot.vue中的代码:

<template>
   <div style="width: 600px; text-align: left;">
       <div style="background: peru" id="test1">
           1. 这段话也是来自defaultSlot.vue,但它写在<code>ChildSlot</code>标签上面,因此,虽然他会有一个棕色的背景色,但不会被红色边框圈起来。
       </div>
       <ChildSlot url="你猜猜看">
           <div style="background: darkgreen" id="test2">
               kankan
           </div>
           <div style="background:deeppink" slot="aaa" id="test3">
               4. aaa这段话写在另一个<code>ChildSlot</code>标签里,会有一个粉色的背景色。
           </div>
           <div style="background: aquamarine" slot="show"  id="test4">
               2. 这是插槽的练习。这段话来自defaultSlot.vue,且会有一个浅绿色的背景色。本页面引入了childSlot.vue;而childSlot.vue中
               有一个<code>slot</code>标签,因此,在这段话的上面,还会显示childSlot.vue中的内容。而如果注释掉childSlot.vue
               中的<code>slot</code>标签,则本段内容不显示。因为这段话写在<code>ChildSlot</code>标签中,因此,它也会被一个红色
               的边框圈起来。
           </div>
           <div style="background: beige"  id="test5">
               5. 我就看看怎么显示我
           </div>
       </ChildSlot>
       <div style="background: gray"  id="test6">
           6. 这段话也是来自defaultSlot.vue,但它写在<code>ChildSlot</code>标签下面,因此,虽然他会有一个灰色的背景色,但不会被红色边框圈起来。
       </div>
       <ChildSlot>
           <div id="test7">
               呵呵呵
           </div>
       </ChildSlot>
   </div>
</template>

    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

childSlot.vue中的代码:

<template>
   <div style="border: 2px solid red;">
       <slot name="show"></slot>
       <span id="test8">3. 这是childSlot.vue中的内容,他会被一个红色的边框圈起来<br/></span>
       <slot name="aaa"></slot>
       <slot></slot>
   </div>
</template>

    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

页面的渲染结果如下图:
在这里插入图片描述
让我们来整理一下加载顺序:
首先,页面主要渲染的是defaultSlot.vue。因此,根据从上到下的渲染顺序,首先加载的就是id="test1"的div;
然后会加载<ChildSlot>标签中的内容,所以要看childSlot.vue中具体是什么样的:
在childSlot.vue中,首先要加载的是name属性为show的部分,也就是id=test4的内容;
然后是id=test8的内容;
然后是name属性为aaa的部分,也就是id=test3的内容;
然后就是默认的slot,在这个<ChildSlot>标签中,就指的是id=test2和id=test5的部分;
这时候,第一个<ChildSlot>标签就渲染结束了,接下来就会加载id=test6的内容;
然后又是一个<ChildSlot>标签,但这次这个标签中,并没有定义show和aaa,所以只显示默认的内容,因为<slot></slot>写在<span>标签下面,因此,先加载id=test8,再加载id=test7;
整个页面就加载完了,而具名插槽和单个插槽的区别应该也能理解了。

作用域插槽

作用域插槽,是为了从父组件中,通过使用注册的子组件标签,并对其属性slot-scope进行定义,从而使用子组件中绑定的数据。
比如我们先在childSlot.vue中,将数据绑定在<slot></slot>标签上:

<template>
    <div style="border: 2px solid red;">
        <slot :data="data"></slot>
    </div>
</template>
<script>
    export default {
        name: "childSlot",
        data(){
            return{
                data:['张三','李四','王五','赵六','钱一','孙二']
            }
        }
    }
</script>

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

然后,我们就可以在defaultSlot.vue中,通过使用<ChildSlot></ChildSlot>标签,来使用子组件中绑定的数据:

<template>
        <div>
            <ChildSlot>
                <template slot-scope="s" slot="aaa">
                    <ul>
                        <li v-for="item in s.data" :key="item">{{item}}</li>
                    </ul>
                </template>
            </ChildSlot>
        </div>
    </div>
</template>
<script>
    import ChildSlot from '../../views/slot/childSlot'
    export default {
        name: "defaultSlot",
        components: {
            ChildSlot
        }
    }
</script>

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

页面中的渲染结果如下:
在这里插入图片描述
让我们来总结一下:
首先在子组件中绑定数据;
然后在父组件中,通过slot-scope来把这个绑定的数据对象进行重命名;
这样就可以在父组件中使用子组件的数据了。

获取当前行的说明

那么再让我们回过头来看看此前获取当前行数据时,最后是怎么处理的:

<el-table-column label="操作" width="300" fixed="right">
	<template slot-scope="scope">
		<el-button type="text" @click="checkDetail(scope.row)">查看详情</el-button>
		<el-button type="text" @click="editDengmi(scope.row)">编辑</el-button>
		<el-button type="text" @click="deleteDengmi(scope.row)">删除</el-button>
	</template>
</el-table-column>

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

结合前文所述,可以知道,这里是使用了作用域插槽,通过**slot-scope=“scope”**将el-table-column标签中绑定的当前行的数据对象重命名为scope,进而再通过scope.row来获取当前行的数据。

查看el-table-column的源文件,在加载之前,对变量其进行了初始化的操作:
在这里插入图片描述
然后在加载数据时,对row进行了赋值:
在这里插入图片描述

                                </div>
            <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-b6c3c6d139.css" rel="stylesheet">
                                            <div class="more-toolbox">
            <div class="left-toolbox">
                <ul class="toolbox-list">
                    
                    <li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true">
                        <use xlink:href="#csdnc-thumbsup"></use>
                    </svg><span class="name">点赞</span>
                    <span class="count">6</span>
                    </a></li>
                    <li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{&quot;mod&quot;:&quot;popu_824&quot;}"><svg class="icon" aria-hidden="true">
                        <use xlink:href="#icon-csdnc-Collection-G"></use>
                    </svg><span class="name">收藏</span></a></li>
                    <li class="tool-item tool-active is-share"><a href="javascript:;"><svg class="icon" aria-hidden="true">
                        <use xlink:href="#icon-csdnc-fenxiang"></use>
                    </svg>分享</a></li>
                    <!--打赏开始-->
                                            <!--打赏结束-->
                                            <li class="tool-item tool-more">
                        <a>
                        <svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg>
                        </a>
                        <ul class="more-box">
                            <li class="item"><a class="article-report">文章举报</a></li>
                        </ul>
                    </li>
                                        </ul>
            </div>
                        </div>
        <div class="person-messagebox">
            <div class="left-message"><a href="https://blog.csdn.net/qq_33616027">
                <img src="https://profile.csdnimg.cn/D/4/E/3_qq_33616027" class="avatar_pic" username="qq_33616027">
                                        <img src="https://g.csdnimg.cn/static/user-reg-year/2x/4.png" class="user-years">
                                </a></div>
            <div class="middle-message">
                                    <div class="title"><span class="tit"><a href="https://blog.csdn.net/qq_33616027" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}" target="_blank">HowYouth</a></span>
                                        </div>
                <div class="text"><span>发布了14 篇原创文章</span> · <span>获赞 15</span> · <span>访问量 1万+</span></div>
            </div>
                            <div class="right-message">
                                        <a href="https://im.csdn.net/im/main.html?userName=qq_33616027" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-letter">私信
                    </a>
                                                        <a class="btn btn-sm  bt-button personal-watch" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}">关注</a>
                                </div>
                        </div>
                </div>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值