【学习前端第六十七课】组件的插槽

组件的插槽

组件本身是一个容器,也是一个vue对象(data,methods…),也是一个虚拟DOM(html标签,但是这些html标签不是既定一些html标签,而是我们创建的所以也叫虚拟标签)

1、普通插槽

组件本身是一个容器,这个容器本身是空的,当我峨嵋你把需要封装的html标签装进这个组件之后,我们可以认为这个组件的内部的容积就直接塞满了,那就意味着,无法再向组件内部添加新的html结构,但是我们想在每次一个调用组件的时候,渲染一点个性化的东西进去,这个时候我们需要给组件去做一个预留空间,这个预留空间就是插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <one>
            <p>我是栏目一的内容</p>
            <!-- 这样插入时没有效果的 -->
        </one>
    </div>
    <template id="temp1">
        <div>
            <h2>我是标题</h2>
        </div>
    </template>
</body>
<script src="js/vue.js"></script>
<script>
    var one = {
        template:"#temp1"
    };
    new Vue({
        el:"#app",
        components:{
            one
        }
    })
</script>
</html>

以上的代码在渲染的时候,我们在虚拟DOM one的里面写入了一个p标签,这就是所谓的插入,但是这个并没有效果,因为我们并没有在one组件的内部留下预留空间,也就是插槽

如果想要在组件内部预留插槽,需要使用 <slot></slot> 的标签完成

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <one>
            <p>我是栏目一的内容</p>
            <p>我是栏目一的内容</p>
            <p>我是栏目一的内容</p>
            <p>我是栏目一的内容</p>
            <p>我是栏目一的内容</p>
        </one>
    </div>
    <template id="temp1">
        <div>
            <h2>我是标题</h2>
            <slot>
                <!-- 
                    <p>我是栏目一的内容</p>
                    <p>我是栏目一的内容</p>
                    <p>我是栏目一的内容</p>
                    <p>我是栏目一的内容</p>
                    <p>我是栏目一的内容</p>
                 -->
            </slot> <!-- 这是预留的插槽 -->
        </div>
    </template>
</body>
<script src="js/vue.js"></script>
<script>
    var one = {
        template:"#temp1"
    };
    new Vue({
        el:"#app",
        components:{
            one
        }
    })
</script>
</html>

代码分析:

在上面的代码里面,我们在one这个虚拟标签插入了5个p标签,这个5个p标签会全部插入到slot的位置

一个插入点插入多个插槽

现在我们想让刚才的代码当中前3个p标签出现在标题的上方,后2个p标签出现在标签的下方,明显刚才的插槽肯定是不能的满足的,那么应该怎么写?

如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <one>
            <p>我是栏目一的内容</p>
            <p>我是栏目一的内容</p>
            <p>我是栏目一的内容</p>
            <p>我是栏目一的内容</p>
            <p>我是栏目一的内容</p>
        </one>
    </div>
    <template id="temp1">
        <div>
            <slot></slot>
            <h2>我是标题</h2>
            <slot>
                <!-- 
                    <p>我是栏目一的内容</p>
                    <p>我是栏目一的内容</p>
                    <p>我是栏目一的内容</p>
                    <p>我是栏目一的内容</p>
                    <p>我是栏目一的内容</p>
                 -->
            </slot> <!-- 这是预留的插槽 -->
        </div>
    </template>
</body>
<script src="js/vue.js"></script>
<script>
    var one = {
        template:"#temp1"
    };
    new Vue({
        el:"#app",
        components:{
            one
        }
    })
</script>
</html>

代码分析:

上面的代码中,渲染的结果我们发现,我们在模板中的标题的上方也制作了一个插件slot,现在的情况,把插入的5个p标签分别插入到了标题上下两个插槽当中,从效果上来看感觉是把冲入的内容复制了一份,分别插入了不同的插槽当中,与我们想要的效果还有差距

其实我们刚才写的都是默认插槽,把这些插槽的语法写完整应该如下

<div id="app">
    <one>
        <p slot="default">我是栏目一的内容</p>
        <p slot="default">我是栏目一的内容</p>
        <p slot="default">我是栏目一的内容</p>
        <p slot="default">我是栏目一的内容</p>
        <p slot="default">我是栏目一的内容</p>
    </one>
</div>
<template id="temp1">
    <div>
        <slot name="default"></slot>
        <h2>我是标题</h2>
        <slot name="default">
            <!-- 
                <p>我是栏目一的内容</p>
                <p>我是栏目一的内容</p>
                <p>我是栏目一的内容</p>
                <p>我是栏目一的内容</p>
                <p>我是栏目一的内容</p>
             -->
        </slot> <!-- 这是预留的插槽 -->
    </div>
</template>

默认情况下所哟䣌slot都必须指定一个name,如果不指定则默认是default

2、具名插槽

在上面的默认插槽中,所有的slot如果不在指定name的情况下都是default,但是这个name是可以设置的,如果设置为一些其他的值,我们就把这个插槽叫做具名插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <one>
            <button type="button" slot="footer">我是footer</button>
            <h3 slot="title">我是标题</h3>
        </one>
    </div>
    <template id="temp1">
        <div>
            <slot name="title"></slot>
            <p>我是一个组件</p>
            <slot name="footer"></slot>
        </div>
    </template>
</body>
<script src="js/vue.js"></script>
<script>
    var one = {
        template:"#temp1"
    };
    new Vue({
        el:"#app",
        components:{
            one
        }
    })
</script>
</html>

代码分析:

在上面的代码当中,我们可以为插槽取一个名字,从而实现一对多,或者多对一的插入,当插槽有了名字之后,我们在插入的时候可以指定插入到某一个插槽

1、我们的组件里面定义插槽的时候使用slot标签,并在这个标签上面定义一个那么属性,这个就是具名插槽

2、在调用组件的时候,可以向指定的插槽位置插入内容,只需要在这个元素上面添加 slot = “插槽名” 即可

3、具名插槽也是可以多次使用的,所以我们把上面的代码中footer改成title,这样标题标签会被插入两次

案例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <app-title :show-back="true">
            软帝点餐
            <span slot="right-menu">+</span>
        </app-title>
        <!-- <app-title>
            登录
            <span slot="right-menu">+</span>
        </app-title> -->
    </div>
    <template id="temp1">
        <div class="page-title">
            <div class="left-back" v-if="showBack">- 返回</div>
            <slot></slot>
            <div class="right-menu">
                <slot name="right-menu"></slot>
            </div>
        </div>
    </template>
</body>
<script src="js/vue.js"></script>
<script>
    var appTitle = {
        template:"#temp1",
        props:{
            showBack:{
                type:Boolean,
                default:() => false
            }
        }
    };
    new Vue({
        el:"#app",
        components:{
            appTitle
        }
    })
</script>
</html>

插槽作用域

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <one>
            <p>我向拿到组件内部的userName</p>
        </one>
    </div>
    <template id="temp1">
        <div class="box">
            <h2>我是一个组件</h2>
            <slot></slot>
        </div>
    </template>
</body>
<script src="js/vue.js"></script>
<script>
    var one = {
        template:"#temp1",
        data(){
            return {
                userName:"zhangsan"
            }
        }
    };
    new Vue({
        el:"#app",
        components:{
            one
        }
    })
</script>
</html>

我们以前一致都是外部的数据传递给组件内部,如果把组件内部的数据传递出去怎么做?

比如我要把userName的值传递到外边,我们可以在插入的标签上添加一个slot-scope=“scope”

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <one>
            <div slot-scope="scope">
                <p>{{scope.userName}}</p>
                <p>{{scope.age}}</p>
            </div>
        </one>
    </div>
    <template id="temp1">
        <div class="box">
            <h2>我是一个组件</h2>
            <slot :user-name="userName" :age="18"></slot>
        </div>
    </template>
</body>
<script src="js/vue.js"></script>
<script>
    var one = {
        template:"#temp1",
        data(){
            return {
                userName:"zhangsan"
            }
        }
    };
    new Vue({
        el:"#app",
        components:{
            one
        }
    })
</script>
</html>

代码分析:

通过上面的例子,我们看到,我们通过slot-scope拿到了插槽里面的作用域,然后拿到了作用域插槽上面的所有的值,然后直接使用渲染

上面的例子使用的是默认插槽,如果是具名插槽怎么办?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <one>
            <div slot="footer" slot-scope="scope">
                <p>{{scope.userName}}</p>
                <p>{{scope.age}}</p>
            </div>
        </one>
    </div>
    <template id="temp1">
        <div class="box">
            <slot></slot>
            <h2>我是一个组件</h2>
            <slot name="footer" :user-name="userName" :age="18"></slot>
        </div>
    </template>
</body>
<script src="js/vue.js"></script>
<script>
    var one = {
        template:"#temp1",
        data(){
            return {
                userName:"zhangsan"
            }
        }
    };
    new Vue({
        el:"#app",
        components:{
            one
        }
    })
</script>
</html>

代码分析:

1、在默认插槽中,我们的数据是直接从外部传递给了userName,同时要注意默认插槽的name就是default所以可以不指定名字

2、在slot="footer"中,我们还向外传递了 :age=“18”

3、在调用组件的时候,我们可以通过slot=“default” 或者是 slot=“footer” 来决定到底插入在什么地方,同时还可以通过slot-scope拿到这个插槽的作用域(通俗点讲就是拿到这个插槽向外传递的数据)

4、这里主要作用的两个属性

​ slot决定插入哪个插槽

​ slot-scope 拿到作用域(拿到插槽上面的数据)

上面的slot-scope是过时的写法,在vue2.6.1版本开始使用新语法v-slot

旧版本语法
<div id="app">
    <one>
        <div slot-scope="scope">
            <p>{{scope.userName}}</p>
            <p>{{scope.age}}</p>
        </div>
    </one>
</div>
<template id="temp1">
    <div class="box">
        <h2>我是一个组件</h2>
        <slot :user-name="userName" :age="18"></slot>
    </div>
</template>
新版本语法
<one>
	<template v-slot:default="scope">
        <p>{{scope.userName}}</p>
        <p>{{scope.age}}</p>
	</template>
    <template v-slot:footer="scope">
        <p>{{scope.userName}}</p>
        <p>{{scope.age}}</p>
	</template>
</one>

代码分析:

1、v-slot是去拿作用域的,如果后面不具名旧拿的是default,所以v-slot:default 就是 v-slot

2、如果要拿具名插槽作用域 v-slot:插槽名称 = “scope”

3、新版本的语法里面只能通过template来在插槽中插入html,不能通过其他标签

最新的语法
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <one>
            <template #footer="{age,sex,userName}">
                <p>{{userName}}</p>
                <p>{{age}}</p>
                <p>{{sex}}</p>
            </template>
        </one>
    </div>
    <template id="temp1">
        <div class="box">
            <slot></slot>
            <h2>我是一个组件</h2>
            <slot name="footer" :age="18" :sex="sex" :user-name="userName"></slot>
        </div>
    </template>
</body>
<script src="js/vue.js"></script>
<script>
    var one = {
        template:"#temp1",
        data(){
            return {
                userName:"zhangsan",
                sex:"女"
            }
        }
    };
    new Vue({
        el:"#app",
        components:{
            one
        }
    })
</script>
</html>

在新版语法中,作用域是可以直接解构获取的,并且v-slot这个指令直接使用#替代,简化代码量,一般我们推荐第一种或第三种写法,第二种做一个了解

注意:

这里会有一个情况,就是作用域在向外传值的时候,动态绑定的属性值有可能会被认为是data的属性

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值