Vue之slot插槽详解

Vue slot插槽

一.slot简介

slot(插槽),我们理解为就是"占位",还有些人可能没那么儒雅,称之为"占坑,茅坑",所以有坑,我们就要填.真正的作用是:在组件模版中占一个坑,当我们使用这个组件标签的时候,通过替换组件模版中slot位置,来填这个坑,并且可以作为承载分发的内容出口,

slot可以干哪些活呢? 如果我们需要父组件在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、怎么显示,就可以使用slot分发

二.slot基础使用

​ 我们知道,在调用组件时,没有使用插槽的情况下,写在组件标签内部的内容是默认不会被渲染的.但是呢,有时候我们需要在组件标签内的内容能够被渲染出来

​ 举个例子:我们做一个弹出框组件,需要做到外部结构固定,弹框内部的结构可以通过不同的场景变化,比如某些地方是否显示关闭按钮,是否显示icon图标,或者弹框中间内容的变化等等.要通过子组件自己封装,肯定是不行的,这个时候就可以使用slot占位,传递dom结构来解决

1.slot内容(插槽内容)

slot内容、插槽内容、插槽模板内容 :调用组件时写在组件标签内的内容就叫做 slot 内容

   <div id="app">
        <!-- slot内容、插槽内容、插槽模板内容 -->
        <hello-slot>默认不被喧嚷</hello-slot>
        <!-- 调用组件时写在组件标签内的内容就叫做 slot 内容 -->
        <!-- 标签内的内容会替换组件模版内容中的slot标签 -->
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('hello-slot',{
            template:`
                <div>
                    <h1>了解插槽</h1>
                    <slot></slot>
                </div>
            `,
        });

        const vm=new Vue({
            el:'#app'
        });
    </script>

标签内的内容会替换组件模版内容中的slot标签

2.slot后备内容(默认内容)

有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。

通俗点说,就是你设置了slot的后备内容,如果调用组件时,你在标签组件

 <div id="app">
        <hello-slot>第一个组件,有内容,会替换slot后备内容</hello-slot>
        <!-- 第二个组件,标签中没有内容,会默认加载slot的后备内容 -->
        <hello-slot></hello-slot>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('hello-slot',{
            template:`
                <div>
                    <h1>slot后备内容</h1>
                    <slot>
                        <p>我是slot的后备内容</p>
                    </slot>
                </div>
            `,
        });

        const vm=new Vue({
            el:'#app'
        });
    </script>

说个案例,比如我们在弹框中,我们一直要保持弹框是默认的按钮文字内容是"保存",但是呢产品经理说要把保存改成"确定",而其他应用此组件的不改,我们这里就可以使用slot的后备内容,默认为文字是"保存",想要改成"确定"时,直接在组件标签中写"确定",就可以实现了

3.slot 具名(具名插槽)

给slot标签设置一个name属性,这个时候slot标签就叫做具名插槽

通俗点说就是:给这个坑起个名字,那么这个坑就叫做有名字的坑

有时候,我们需要使用多个插槽,也就意味着slot在一个组件中,是可以使用多次的

给slot取好名字之后,slot内容想要渲染在哪个slot里面,就需要设置slot属性,属性的值为某个slot的名字,另外需要注意的是:slot有个默认的名字叫做"default"

<!--  二者全等于  -->
<slot></slot>   ===  <slot name="default"></slot>

下面这种方式在vue 2.6.0版本已经废弃了,但是还是可以使用,也就是通过使用slot属性的形式

 <div id="app">
        <hello-slot>
            <!-- 使用slot属性 -->
            <p slot="hello">名字为hello的插槽</p>
            <p slot="world">名字为world的插槽</p>
            <p>默认default的插槽</p>
        </hello-slot>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('hello-slot', {
            template: `
                <div>
                    <h1>slot具名</h1>
                    <slot name="hello"></slot>
                    <slot name="world"></slot>
                    <slot name="default"></slot>
                    <slot></slot>
                </div>
            `,
        });

        const vm = new Vue({
            el: '#app'
        });
    </script>

下面是2.6.0之后的具名插槽使用用法

 <div id="app">
        <hello-slot>
            <!-- 注意:v-slot指令只能作用在template标签上(除了一种例外:
            当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用) -->
            <template v-slot:hello>
                <p>名字为hello的插槽</p>
            </template>
            <!-- v-slot的简写是 # -->
            <template #world>
                <p>名字为world的插槽</p>
            </template>

            <!-- <template #default>
                <p>名字为default的插槽</p>
            </template> -->

            <!-- 如果没有被带有v-slot指令的template包裹,则会被当做slot的默认内容,当template没有使用 默认名字的插槽时会被喧嚷 -->
            <p>A paragraph for the main content.</p>
            <p>And another one.</p>

            <!-- 另外:template使用插槽时,不能同时使用多次,会覆盖掉之前的template -->
            <template>
                <p>如果没有被template包裹</p>
            </template>

        </hello-slot>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('hello-slot', {
            template: `
                <div>
                    <h1>slot具名</h1>
                    <slot name="hello"></slot>
                    <slot name="world"></slot>
                    <slot name="default"></slot>
                    <slot></slot>
                </div>
            `,
        });

        const vm = new Vue({
            el: '#app'
        });
    </script>

总结几点:

  • 注意:v-slot指令只能作用在template标签上(除了一种例外:当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用

  • v-slot的简写是 #

      ```js
      <template #world>
         <p>名字为world的插槽</p>
      </template>
      
      <!-- 全等于  -->
      <template v-slot:world>
            <p>名字为world的插槽</p>
      </template>       
      ```
    
  • 如果没有被带有v-slot指令的template包裹,则会被当做slot的默认内容,当template没有使用 默认名字的插槽时会被喧嚷 注意:必须要带v-slot指令,只有template包裹也不行!!!还是会被当做默认内容

  • template使用插槽时,不能同时使用多次,会覆盖掉之前的template

      <template #world>
           <p>名字为world的插槽</p>
      </template>
          <!-- 另外:template使用插槽时,不能同时使用多次,会覆盖掉之前的template -->
      <template #world>
             <p>名字为world的插槽  此处会覆盖上面的内容</p>
       </template>
    
  • slot没有设置name属性时,默认还是default

    <template #default>
       <p>名字为default的插槽</p>
    </template>
    

4.slot编译作用域

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

<div id="app">
       // app节点里包含的内容都是父级作用域
        <hello-slot>
            <p>{{msg}}</p>
        </hello-slot>

        <hello-slot>
            <template #hello>
                <p>{{msg}}</p>
            </template>
        </hello-slot>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('hello-slot', {
        //子组件中的template内容都是子作用域
            template: `
                <div>
                    <h1>slot编译作用域</h1>
                    <slot name="hello"></slot>
                    <slot>
                        <p>{{msg}}</p>    
                    </slot>
                </div>
            `,
            data() {
                return {
                    msg: 'hello'
                }
            }
        });

        const vm = new Vue({
            el: '#app',
            data: {
                msg: "root"
            }
        });
    </script>

上述代码,都会打印一个msg,但是由于slot编译作用域的关系,打印的msg属于父组件还是子组件,是值得考虑的,上面说了,父级编译父级的,子级编译子级的

总结一句话就是:虽然说插槽内容最终渲染在子组件内,但是它的编译作用域还是要看插槽内容写在哪个组件的template里面.而不是看插槽内容最终渲染在哪个组件的template里面的slot标签

注意理解:写在组件标签内的插槽内容是不属于组件的template的,只是通过slot可以让他渲染在组件里面去.

其实这么看就行了:app节点里包含的内容都是父级作用域,而子组件中的template选项内容都是子作用域

5.作用域插槽

作用域插槽的概念是:希望父组件的插槽内容使用子组件的作用域

 	<div id="app">
        <hello>
          // xxxx 如何能够用上 hello 组件中 的 数据
          <p>我是一个插槽内容。{{xxxx}} </p>
        </hello>
      </div>

因为slot编译作用域的缘故,父组件的插槽内容不能直接获取子组件作用域,所以这里需要使用作用域插槽

使用方式如下:

  • 定义slot的时候,将要在插槽内容中使用的数据,绑定在slot标签上,注意:不能绑定name属性,因为name属性有特殊作用,name属性用来具名插槽

    <slot :age="age" :sex="sex" ></slot>
    
  • 在插槽内容的标签上,设置slot-scope尚属性,属性值自定义,其实就是在slot标签绑定的属性上,封装成了一个大对象

    <!-- obj==={age:'',sex=''}  -->
    <p slot-scope="obj">{{age}} {{sex}}</p>
    
  • 解构赋值的用法

    <!-- 还有一种方式,解构赋值 -->
    <p slot-scope={age,sex}>{{ age }} {{ sex }}</p>
    

来个示例:

 <div id="app">
        <hello-slot>
            <!-- 
                使用slot-scope属性,值是一个对象,对象里包含子组件中slot传过来的值 
                obj === { a: "hello", b: 19 }
            -->
            
            <!-- <p slot-scope="obj">{{ obj.msg }} {{ obj.age }}</p> -->

            <!-- 还有一种方式,解构赋值 -->
            <p slot-scope={msg,age}>{{ msg }} {{ age }}</p>
        </hello-slot>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('hello-slot', {
            template: `
                <div>
                    <h1>slot作用域插槽</h1>
                    <slot :msg="msg"  :age="age"></slot>
                </div>
            `,
            data() {
                return {
                    msg: 'hello',
                    age: 19,
                }
            }
        });

        const vm = new Vue({
            el: '#app',
        });
    </script>

还需要注意:使用slot-scope属性也是在2.6.0之后废弃了的,但是很多公司还是用的之前版本,下面是2.6.0之后的新语法

  <div id="app">
        <hello-slot>
            <!-- 没有名字,默认default -->
            <template #default="obj">
                <p>{{obj.msg}}</p>
            </template>
            <!-- 具名插槽 -->
            <template #hello="hello">
                <p>{{hello.msg}}</p>
            </template>

        </hello-slot>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('hello-slot', {
            template: `
                <div>
                    <h1>slot作用域插槽</h1>
                    <slot name='hello' :msg="msg"></slot>
                    <slot :msg="msg"></slot>
                </div>
            `,
            data() {
                return {
                    msg: 'hello',
                }
            }
        });

        const vm = new Vue({
            el: '#app',
        });
    </script>

6.扩展新语法

  • slot 插槽 在 2.6.0 这个版本的时候,做了更新。提供了一个新的指令叫做 v-slot。后续实现具名插槽与作用域插槽都使用 v-slot 来实现

  • 2.6.0 版本在什么时候发布的:2019-02-04号(农历2018年过年那天)

  • v-slot语法

     v-slot:xxxx=yyyy
              - xxxx    插槽名字
              - yyyy    作用域插槽数据
          
      如下:
      <template #hello="hello">
             <p>{{hello.msg}}</p>
      </template>
    

    1.v-slot 必须用在 template 元素上。也就是说 插槽内容 需要通过 template 元素进行包裹

    2.如果插槽没有设置 name 名字那v-slot === v-slot:default

    3.v-bind 指令有个简写 :
    v-on 指令有个简写 @
    v-slot 指令有个简写 # (注意,使用 简写时必须携带名字。 默认的名字需要写成 #default)

    4.插槽只有一个的情况下,可以不使用 template 去包裹插槽内容。而是直接将 v-slot 写在组件标签上。

    //子组件传过来的,可以v-slot 写在组件标签上,解构赋值
    <hello-slot v-slot="{user}">
      {{user.name}}
    </hello-slot>
    
     
    	Vue.component('hello-slot', {
                template: `
                    <div>
                        <slot :user='user'></slot>
                    </div>
                `,
                data() {
                    return {
                        user: {
                            name: 'liuqiao'
                        }
                    }
    
                }
            });
    

猜你喜欢

转载自blog.csdn.net/liuqiao0327/article/details/106727059