详解Vue中的插槽
作者: 小土豆
博客园:https://www.cnblogs.com/HouJiao/
掘金:https://juejin.im/user/2436173500265335
什么是插槽
在日常的项目开发中,当我们在编写一个完整的组件时,不可避免的会引用一些外部组件
或者自定义组件
。
有了这种引用关系
之后,我们就可以把它们称为父组件
或者子组件
,同时父子组件
之间有很多的通信方式,比如可以通过props
向子组件
传递数据,或者通过$emit
、$parent
调用父组件
中的方法。
下面就是一个非常简单的父组件
引用子组件
的例子。
<!-- 子组件: /slot-demo/src/components/Child.vue -->
<template>
<div class="child">
<!-- 标记 -->
<div>
<i class="el-icon-s-flag"></i>Badge 标记
<div class="content">
<el-badge :value="12" class="item">
<el-button size="small">评论</el-button>
</el-badge>
</div>
</div>
<!-- 进度条 -->
<div>
<i class="el-icon-s-flag"></i>进度条
<div class="content">
<el-progress :percentage="50"></el-progress>
</div>
</div>
</div>
</template>
<!-- 省略其它代码 -->
接着我们在App
组件中引用Child
组件。
<!-- 父组件: /slot-demo/src/App.vue -->
<template>
<div id="app">
<!-- 使用子组件 -->
<child></child>
</div>
</template>
<script>
import Child from './components/Child.vue'
export default {
name: 'App',
components: {
Child
}
}
</script>
最后运行项目,子组件
的内容成功被引用并展示在页面上。
那假如我们现在有这样一个需求:在引用Child
组件的同时,希望在Child
组件的指定位置
插入一段内容:<h1> 欢迎大家关注小土豆 </h1>
。
如果我们直接将内容写入<child></child>
内部,是不会生效的。
<!-- 父组件: /slot-demo/src/App.vue -->
<template>
<div id="app">
<!-- 使用子组件 -->
<child>
<h1> 欢迎大家关注小土豆 </h1>
</child>
</div>
</template>
<script>
import Child from './components/Child.vue'
export default {
name: 'App',
components: {
Child
}
}
</script>
可以看到并未达到预期效果:
那为了解决类似这样的问题,Vue
就设计出来了slot
这个东西。slot
翻译过来叫做插槽
,也可称其为Vue
的内容分发机制,它的主要作用就是向子组件
的指定位置
插入一段内容,这个内容可以是HTML
或者其他的组件
。
默认插槽
在前面一节内容里,我们提出了一个需求:在引用Child
组件的同时,希望在Child
组件的指定位置
插入一段内容:<h1> 欢迎大家关注小土豆 </h1>
。
那这个需求如何使用插槽
来实现呢?我们来实践一下。
首先我们需要在子组件
中写入<slot></slot>
,同时这个在<slot>
标签内部可以有默认的内容,比如<slot>我是这个slot里面本来的内容</slot>
<!-- 子组件: /src/components/Child.vue -->
<template>
<div class="child">
<!-- 标记 -->
<div>
<i class="el-icon-s-flag"></i>Badge 标记
<div class="content">
<el-badge :value="12" class="item">
<el-button size="small">评论</el-button>
</el-badge>
</div>
</div>
<!-- 进度条 -->
<div>
<i class="el-icon-s-flag"></i>进度条
<div class="content">
<el-progress :percentage="50"></el-progress>
</div>
</div>
<!-- 占位符 -->
<slot>我是这个slot里面本来的内容</slot>
</div>
</template>
<!-- 省略其它代码 -->
接着就是在父组件
中传入我们希望插入到子组件
中的内容。
<!-- 父组件: /src/App.vue -->
<template>
<div id="app">
<!-- 使用子组件 -->
<child>
<h1> 欢迎大家关注小土豆 </h1>
</child>
</div>
</template>
<script>
import Child from './components/Child.vue'
export default {
name: 'App',
components: {
Child
}
}
</script>
此时在运行项目,就能看到<h1> 欢迎大家关注小土豆 </h1>
这段内容已经成功的显示在页面上。
具名插槽
具名插槽
就是给我们的插槽
起一个名字,即给<slot></slot>
定义一个name
属性。
<!-- 插槽名称为:heading -->
<slot name="heading"></slot>
<!-- 插槽名称为:sub-heading -->
<slot name="sub-heading"></slot>
<!-- 插槽名称为:footer-text -->
<slot name="footer-text"></slot>
给插槽
起了名称以后,我们在父组件
中就可以使用v-slot:name
或者#name
往指定的插槽
填充内容。
#name
是v-slot:name
的简写形式
下面我们就来实践一下具名插槽
。
首先是在子组件(Child.vue)
中定义具名插槽
。
<!-- 子组件: /slot-demo/src/components/Child.vue -->
<template>
<div class="child">
<!-- 插槽名称为:heading -->
<slot name="heading"></slot>
<!-- 插槽名称为:sub-heading -->
<slot name="sub-heading"></slot>
<!-- 标记 -->
<div>
<i class="el-icon-s-flag"></i>Badge 标记
<div class="content">
<el-badge :value="12" class="item">
<el-button size="small">评论</el-button>
</el-badge>
</div>
</div>
<!-- 进度条 -->
<div>
<i class="el-icon-s-flag"></i>进度条
<div class="content">
<el-progress :percentage="50"></el-progress>
</div>
</div>
<!-- 插槽名称为:footer-text -->
<slot name="footer-text"></slot>
</div>
</template>
<!-- 省略其它代码 -->
接着在父组件(App.vue)
中使用。
<!-- 父组件: /slot-demo/src/App.vue -->
<template>
<div id="app">
<!-- 使用子组件 -->
<child>
<template v-slot:heading>
<h1>element-ui组件</h1>
</template>
<template v-slot:sub-heading>
<p>这里是element-ui的部分组件介绍</p>
</template>
<template v-slot:footer-text>
<p>出品@小土豆</p>
</template>
</child>
</div>
</template>
<script>
import Child from './components/Child.vue'
export default {
name: 'App',
components: {
Child
}
}
</script>
运行项目就能看到对应的内容被插入到对应的插槽内:
补充内容——默认插槽的name
属性
其实关于前面的默认插槽
它也是有name
属性的,其值为default
,所以在父组件
中也可以这样写:
<!-- 父组件: /slot-demo/src/App.vue -->
<child>
<template v-slot:defalut>
<h1> 欢迎大家关注小土豆 </h1>
</template>
</child>
补充内容——<template>
元素上使用 v-slot
指令
在演示具名插槽
的时候,我们的v-slot
是写在<template>
元素上的,这个是比较推荐的写法,因为<template>
在处理的过程中不会渲染成真实的DOM
节点。
<template v-slot="default">
<h1>欢迎关注小土豆</h1>
</template>
处理之后的DOM
节点:
<h1 data-v-2dcc19c8="">欢迎关注小土豆</h1>
当然我们也可以将v-slot
应用在其他的HTML
元素上,这样最终插入到子组件中的内容就会有一层真实的DOM
节点包裹。
<div class="text" v-slot="default">
<h1>欢迎关注小土豆</h1>
</div>
处理之后的DOM
节点:
<div data-v-2dcc19c8="" class="text">
<h1 data-v-2dcc19c8="">欢迎关注小土豆</h1>
</div>
作用域插槽
关于作用域插槽
的相关概念和示例看了很多,但相对于前面两种类型的插槽来说,确实有些难以理解。如果需要用一句话去总结作用域插槽
,那就是在父组件中访问子组件的数据
,或者从数据流向
的角度来讲就是将子组件的数据传递到父组件
。
一个新概念或者一个新技术的出现总是有原因的,那作用域插槽
的出现又是为了解决什么样的问题呢?一起来研究一下吧。
作用域插槽的使用
我们先来看看如何利用作用域插槽
实现在父组件中访问子组件的数据
。
首先我们需要在子组件
的插槽<slot><slot>
上使用v-bind
绑定对应的数据。
<!-- 子组件: /slot-demo/src/components/Child.vue -->
<template>
<div class="child">
<slot
name="heading"
v-bind:headingValue="heading">
{{heading}}
</slot>
<!-- 为了让大家看的更清楚 已经将Child.vue组件中多余的内容删除 -->
</div>
</template>
<script>
export default {
name: 'Child',
data() {
return {
heading: '这里是默认的heading'
}
}
}
</script>
可以看到我们在<slot>
上使用v-bind
绑定了vue data
中定义的heading
数据。
接着我们就可以在父组件
中定义一个变量
来接收子组件
中传递的数据。
父组件中接收数据的
变量名
可以随意起,这里我起的变量名为slotValue
<!-- 父组件: /slot-demo/src/App.vue -->
<template>
<div id="app">
<child>
<template v-slot:heading="slotValue" >
<h1>element-ui组件</h1>
slotValue = {{slotValue}}
</template>
</child>
</div>
</template>
运行项目后查看页面的结果:
可以看到slotValue
是一个对象,保存了一组数据,其键
就是我们在子组件
的<slot>
上使用v-bind
绑定的属性名headingValue
,其值
是v-bind
绑定的heading
值。
作用域插槽的应用场景
前面我们了解了作用域插槽
的用法,也得知其主要目的是为了能在父组件
中访问子组件
的数据。那什么时候父组件
需要访问子组件
的数据呢。
我们来举个简单的栗子。
假设我们有下面这样一个Card
组件:
<!-- Card组件:/slot-demo/src/components/Card.vue -->
<template>
<div class="card">
<h3>{{title}}</h3>
<p v-for="item in list" :key="item.id">
{{item.id}}.{{item.text}}
</p>
</div>
</template>
<script>
export default {
name: 'Card',
props: ['title', 'list'],
data() {
return {
}
}
}
</script>
<style scoped>
.list{
border: 1px solid;
padding: 20px;
}
.list p{
border-bottom: 2px solid #fff;
padding-bottom: 5px;
}
</style>
其中Card
组件中展示的title
和list
数据由父组件
传入。
接着在App
组件中复用Card
组件,并且传入title
和list
数据。
<!-- App组件:/slot-demo/src/App.vue -->
<template>
<div id="app">
<card :list="list" :title="title">
</card>
</div>
</template>
<script>
import Card from './components/Card.vue'
export default {
name: 'App',
components: {
Card,
},
data() {
return {
title: '名人名言',
list:[
{
id:1,
text:'要成功,先发疯,头脑简单向前冲'
},{
id:2,
text:'不能天生丽质就只能天生励志!'
},{
id:3,
text:'世上唯一不能复制的是时间,唯一不能重演的是人生。'
}
]
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* text-align: center; */
/* color: #2c3e50; */
/* margin-top: 60px; */
color: #fff;
background: rgba(232, 0, 0, 0.3);
padding: 20px;
}
</style>
运行项目查看页面:
Card
组件本身并不复杂,就是展示title
和list
里面的数据。但凡是有相同需求的都可以通过复用Card
组件来实现。
但是仔细去想,我们的Card
组件其实并没有那么灵活:如果有些页面需要复用
该组件,但是希望在title
处增加一个图标
;或者有些页面需要在展示内容时候不显示编号1、2、3
。
那这样的需求使用插槽
就可以轻松实现。
<!-- Card组件:/slot-demo/src/components/Card.vue -->
<template>
<div class="card">
<h3>
<slot name="title" v-bind:titleValue="title"> {{title}} </slot>
</h3>
<p v-for="item in list" :key="item.id">
<slot name="text" v-bind:itemValue="item">{{item.id}}.{{item.text}}</slot>
</p>
</div>
</template>
我们在Card
组件展示title
和list
的位置分别添加了对应的具名插槽
,并且通过v-bind
将title
、item
(list
循环出来的数据)传递给了父组件
。
此时父组件
就可以控制子组件
的显示。
假如我们需要在title
处添加图标,则App
组件复用Card
组件的方式如下:
<!-- App组件:/slot-demo/src/App.vue -->
<card :list="list" :title="title">
<template v-slot:title="slotTitle">
<i class="el-icon-guide"></i>{{slotTitle.titleValue}}
</template>
</card>
页面效果:
亦或者有些页面需要在展示内容时候不显示编号1、2、3
:
<!-- App组件:/slot-demo/src/App.vue -->
<card :list="list" :title="title">
<template v-slot:text="slotItem">
{{slotItem.itemValue.text}}
</template>
</card>
页面效果:
这里应该能想起来
element table
组件的实现方式,是不是也有点这样的意思呢
到这里或许有人会说这样的需求不用插槽也能实现,直接在Card
组件中增加一些逻辑即可。这样的说法固然是可以实现功能,但是显然不是一个好办法。
因为组件的设计本身是希望拿来复用的,如果这个组件本身大部分实现是符合我们的需求的,只有一小部分不符合,我们首先应该想要的是去扩展该组件
,而不是修改组件
,这也是软件设计的思想:开放扩展,关闭修改
。所以插槽
的出现正是对组件的一种扩展,让我们可以更加灵活的复用组件。
废弃的插槽语法
关于以上所描述的插槽
语法,均是vue 2.6.0
以后的语法。在这之前,插槽的语法为slot(默认插槽或者具名插槽)
和slot-scope(作用域插槽)
。
默认插槽
<!-- Card组件:/slot-demo/src/components/Card.vue -->
<!-- 子组件的写法依然不变 -->
<slot></slot>
<!-- App组件:/slot-demo/src/App.vue -->
<child>
<template>
<h1>欢迎关注小土豆</h1>
</template>
<child>
<p>或者</p>
<child>
<template slot="default">
<h1>欢迎关注小土豆</h1>
</template>
<child>
页面效果:
具名插槽
<!-- Card组件:/slot-demo/src/components/Card.vue -->
<!-- 子组件的写法依然不变 -->
<!-- 插槽名称为:heading -->
<slot name="heading"></slot>
<!-- 插槽名称为:sub-heading -->
<slot name="sub-heading"></slot>
<!-- 插槽名称为:footer-text -->
<slot name="footer-text"></slot>
<!-- App组件:/slot-demo/src/App.vue -->
<child>
<template slot="heading">
<h1>element-ui组件</h1>
</template>
<template slot="sub-heading">
<p>这里是element-ui的部分组件介绍</p>
</template>
<template slot="footer-text">
<p>出品@小土豆</p>
</template>
</child>
页面效果:
作用域插槽
<!-- 子组件: /src/components/Child.vue -->
<slot
name="heading"
v-bind:headingValue="heading">
{{heading}}
</slot>
<!-- 父组件: /slot-demo/src/App.vue -->
<child>
<template slot="heading" slot-scope="headingValue" >
<h1>element-ui组件</h1>
headingValue = {{headingValue}}
</template>
</child>
页面效果:
总结
到这里本篇文章就结束了,内容非常简单易懂,可以是茶余饭后的一篇知识回顾。
最后我们在来做一个小小的总结:
近期文章
骨架屏(page-skeleton-webpack-plugin)初探
Vue结合Django-Rest-Frameword实现登录认证(二)
Vue结合Django-Rest-Frameword实现登录认证(一)
写在最后
如果这篇文章有帮助到你,️关注+点赞️鼓励一下作者
文章公众号
首发,关注 不知名宝藏程序媛
第一时间获取最新的文章
笔芯️~
详解Vue中的插槽的更多相关文章
- 详解Vue中的computed和watch
作者:小土豆 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.cn/user/2436173500265335 1. 前言 作为一名Vue ...
- 详解Vue中watch的高级用法
我们通过实例代码给大家分享了Vue中watch的高级用法,对此知识点有需要的朋友可以跟着学习下. 假设有如下代码: <div> <p>FullName: {{fullName} ...
- 详解Vue中的nextTick
Vue中的nextTick涉及到Vue中DOM的异步更新,感觉很有意思,特意了解了一下.其中关于nextTick的源码涉及到不少知识,很多不太理解,暂且根据自己的一些感悟介绍下nextTick. 一. ...
- 详解Vue中的虚拟DOM
摘要: 什么是虚拟DOM? 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 Vue.js 2.0引入Virtual DOM,比Vue.js 1.0的初始渲染速度提升了2-4倍,并 ...
- 详解JavaScript中的原型
前言 原型.原型链应该是被大多数前端er说烂的词,但是应该还有很多人不能完整的解释这两个内容,当然也包括我自己. 最早一篇原型链文章写于2019年07月,那个时候也是费了老大劲才理解到了七八成,到现在 ...
- 详解vue的数据binding原理
自从angular火了以后,各种mv*框架喷涌而出,angular虽然比较火,但是他的坑还是蛮多的,还有许多性能问题被人们吐槽.比如坑爹的脏检查机制,数据binding是受人喜爱的,脏检查就有点…性能 ...
- 详解Vue 方法与事件处理器
本篇文章主要介绍了详解Vue 方法与事件处理器 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 方法与事件处理器 方法处理器 可以用 v-on 指令监听 DOM 事件 ...
- 详解Vue的slot新用法
摘要: 理解Vue插槽. 作者:前端小智 原文:vue 2.6 中 slot 的新用法 Fundebug经授权转载,版权归原作者所有. 为了保证的可读性,本文采用意译而非直译. 最近发布不久的Vue ...
- 详解vue 路由跳转四种方式 (带参数)
详解vue 路由跳转四种方式 (带参数):https://www.jb51.net/article/160401.htm 1. router-link ? 1 2 3 4 5 6 7 8 9 10 ...
随机推荐
- 华为交换机eNSP删除Vlan的详细步骤
设备支持批量删除VLAN和单个删除VLAN两种方式: 单个删除VLAN10 <HUAWEI> system-view [HUAWEI] undo vlan 10 批量删除VLAN10到VL ...
- 新手上路A4——多JDK环境变量的配置
目录 配置单个JDK的方法 配置2+JDK的方法 方法 补充 检查JDK版本是否切换成功 前面讲了如何选择Java版本. 以及JDK8和JDK11的下载安装配置 有想法的人就开始发动他们优秀的小脑袋瓜 ...
- 阿里云ECS服务器连接MongoDB
第一次接触MongoDB,第一次部署.将一些步骤整理出来,希望以后会用到,也希望能帮组到有这方面需求的小伙伴. 设备说明: 服务器为阿里云ECS服务器,网络为专有网络VPC,Mango为买的阿里云Ma ...
- npm常用操作
Npm常用操作 1. 淘宝镜像 1.1 npm临时使用淘宝镜像安装依赖包 npm i -g express --registry https://registry.npm.taobao.org 1.2 ...
- PyQt(Python+Qt)学习随笔:Qt中的部分类型QString、QList和指针、引用在PyQt中的实现方式
老猿Python博文目录 老猿Python博客地址 在我们查阅Qt的文档资料时,可以看到Qt中的链表使用的是QList,字符串使用的是QString,但老猿在测试时发现这两个类型PyQt不支持,无法找 ...
- 刷题记录:[GWCTF 2019]枯燥的抽奖
目录 刷题记录:[GWCTF 2019]枯燥的抽奖 知识点 php伪随机性 刷题记录:[GWCTF 2019]枯燥的抽奖 题目复现链接:https://buuoj.cn/challenges 参考链接 ...
- flask注册蓝图报错
记录下这个我找了两天的坑... take no arguments() 这两天一直学习flask的时候,我把注册的蓝图,写成注册的form表单的 举个栗子 class TetsView(view.Me ...
- 庐山真面目之七微服务架构Consul集群、Ocelot网关集群和IdentityServer4版本实现
庐山真面目之七微服务架构Consul集群.Ocelot网关集群和IdentityServer4版本实现 一.简介 在上一篇文章<庐山真面目之六微服务架构Consul集群.Ocelot网 ...
- Vue项目上线环境部署,项目优化策略,生成打包报告,及上线相关配置
Node.js简介 Node.js是一个基于Chrome V8引擎的JavaScript运行环境,用来方便快速地搭建易于扩展的网络应用.Node.js使用了一个事件驱动.非阻塞式I/O的模型,使其轻量 ...
- Day5 - 01 函数及函数的调用概念
函数就是最基本的一种代码抽象的方式.函数只需写一次,就可以多次调用.Python本身内置了很多有用的函数,可以直接调用. 调用函数 要调用一个函数,需要知道函数的名称和参数.可以通过help(x ...