Vue slot 插槽用法:自定义列表组件
Vue 框架的插槽(slot)功能相对于常用的 v-for, v-if 等指令使用频率少得多,但在实现可复用的自定义组件时十分有用。例如,如果经常使用前端组件库的话,就会经常看到类似的用法:
<card>
<template slot="title">定制卡片组件的标题栏,例如插入icon、操作按钮等</template>
</card>
之前在写前端时,发现产品原型的多个页面中多次出现了基本一致的信息栏,如下图。如果只在一个页面中出现一两次,复制几遍列表代码,写一套样式也关系不大;但在多个页面中来回复制粘贴就很麻烦,增加无效代码量,以后也不好修改(众所周知,前端 UI 修改并不罕见)。因此将这个信息栏抽象成一个组件,可以多次复用,实现内容与样式分离。接下来借这个例子分享一下 Vue 插槽的几种主要用法。

基本用法
默认插槽
首先新建 InfoCard.vue 组件,写好基本的模板结构和样式:上面一条标题栏,下面一个列表(项目中用的是 antd 组件库中的组件,只是样式,不影响理解)。CSS 不重要就不贴了。
<!-- InfoCard.vue -->
<template>
<div class="side-card">
<div class="side-card-title">这里是标题</div>
<a-list>
<a-list-item>
<slot></slot>
</a-list-item>
</a-list>
</div>
</template>
在页面中引入组件后可以在模板中用标签使用:
<!-- index.vue -->
<info-card>Hello</info-card>
由于组件中只有一个 <slot> 元素(也就是“插槽”),标签内的内容就会被“插入”插槽对应的位置:

具名插槽
如果组件中有不止一个插槽,就需要通过名字来区分。
- 在组件中,使用
<slot name="xx">属性指定插槽的名字 - 在页面中,使用
<template slot="xx">属性将内容分发到对应的插槽
<!-- InfoCard.vue -->
<div class="side-card">
<div class="side-card-title" v-if="!hideTitle">
<slot name="title"></slot>
</div>
<a-list>
<a-list-item>
<slot name="content"></slot>
</a-list-item>
</a-list>
</div>
<!-- index.vue -->
<info-card>
<template slot="title">
<p>Hello <a-icon type="smile" /></p>
</template>
<template slot="content"> world </template>
</info-card>

缺省内容
有时候组件的可变部分有默认值,并不必须在使用时指定(例如默认提示语)。在组件的 <slot> 标签内部的内容就是该插槽的缺省内容,如果在使用时没有传入相应内容,将使用缺省内容进行渲染。
在这个例子中,标题部分多数情况下是纯文本,少数情况下才需要使用 HTML 进行定制(例如包含操作按钮)。因此可以配合组件的传入参数,让标题定义变得更简洁,不需要为了一行文本去写整个标签。(顺便增加一个参数可以隐藏标题栏。)
提示:组件传参的时候注意区分
title="xxx"和:title="xxx",平时写多了冒号容易手滑。加冒号是简写的v-bind指令,这个“xxx”代表的是 data 中一个叫做 xxx 的变量;不加冒号的才是传入字符串“xxx”作为参数 title 的值。
<!-- InfoCard.vue -->
<div class="side-card">
<div class="side-card-title" v-if="!hideTitle">
<slot name="title">{{ title }}</slot>
</div>
<a-list>
<a-list-item>
<slot name="content"></slot>
</a-list-item>
</a-list>
</div>
<!-- index.vue -->
<info-card title="Hello">
<template slot="content">world</template>
</info-card>
作用域
列表组件应该接收一个数组作为参数,使用 v-for 循环显示,并且每个列表项的具体内容由页面传入的插槽内容决定(因为不同列表里的对象不一致)。
<a-list>
<a-list-item v-for="item in items" :key="item.id">
<slot name="content"></slot>
</a-list-item>
</a-list>
但是如果在页面中这样使用,会产生报错 Cannot read property 'tag' of undefined。
<info-card title="Hello" :items="hotTags">
<template slot="content">
<a-tag># {{ item.tag }}</a-tag>
<span class="number">{{ item.count }}</span>
</template>
</info-card>
产生错误的原因在于,父页面插槽中的内容先在父页面中渲染,之后才整体插入子组件的插槽;而不是先插入 HTML 后再一起渲染。很显然,items、item 都是定义在子组件中的变量数据,在父组件中没有定义,自然也无法访问(父页面中的数据是 hotTags)。
插槽 prop
这里使用的是 Vue 2.6.0 起更新的语法,原来的作用域插槽
slot-scope属性已弃用
页面传递给子组件的参数作用域在子组件内部,而列表项的内容需要在父页面中定义;因此,需要一种在父组件访问子组件数据的机制。这就是插槽 prop 的作用。

在子组件的 <slot> 标签中使用 v-bind 绑定的属性就是插槽 prop(这里为了清晰才区分命名了 itemprop 和 item,其实实际用的时候全命名成一样的即可,省的倒来倒去)。
页面使用组件时,通过命令 v-slot:name="slotProps" 即可通过 slotProps 访问 name 插槽中绑定的插槽 prop。
<!-- InfoCard.vue -->
<a-list>
<a-list-item v-for="item in items" :key="item.id">
<slot name="content" :itemprop="item">
{{ item }}
</slot>
</a-list-item>
</a-list>
<!-- index.vue -->
<info-card hideTitle :items="hotTags">
<template v-slot:content="props">
<a-tag># {{ props.itemprop.tag }}</a-tag>
<span class="number">{{ props.itemprop.count }}</span>
</template>
</info-card>

注意:如前文所述,插槽内容是在父页面中渲染的。因此其中元素的样式(例如这里 a-tag 的样式)也应该定义在父页面中。
简写
v-slot:指令可以简写为#- 可以使用ES2015 解构解析插槽 prop 中的各个属性,更加清晰简洁
<template #content="{ itemprop }">
<a-tag># {{ itemprop.tag }}</a-tag>
<span class="number">{{ itemprop.count }}</span>
</template>
结语
以上是借助自定义表单组件案例对 Vue 插槽基本用法的介绍,希望对你有所帮助,如有疏漏欢迎留言指正讨论。
文末附上开头图片中信息栏案例的大部分实现代码,可以对照进行参考。
参考资料:Vue slot
附录
以下是 InfoCard.vue 的全部代码:
<template>
<div class="side-card">
<div class="side-card-title" v-if="!hideTitle">
<slot name="title">{{ title }}</slot>
</div>
<a-list>
<a-list-item v-for="item in items" :key="item.id">
<slot name="content" :item="item">
{{ item }}
</slot>
</a-list-item>
</a-list>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '',
},
hideTitle: {
type: Boolean,
default: false,
},
items: {
type: Array,
required: true,
},
},
}
</script>
<style lang="less" scoped>
.side-card {
border-radius: 4px;
background-color: @item-background;
.side-card-title {
height: 36px;
line-height: 36px;
padding: 0 20px;
color: #ffffff;
border-radius: 4px 4px 0 0;
background: linear-gradient(90deg, #1375ff 0%, #a4fffa 149.57%);
display: flex;
justify-content: space-between;
}
.ant-list {
padding: 0 20px;
.ant-list-item {
display: flex;
}
}
}
</style>
以下是实现开头三个信息栏的父页面代码(缺少一些 icon):
<list-card hideTitle :items="myData" class="side-card">
<template #content="{ item }">
<span>{{ item.title }}</span>
<a-tag class="number">{{ item.count }}</a-tag>
</template>
</list-card>
<list-card title="本周热搜 TOP5" :items="hotTags" class="side-card">
<template #content="{ item }">
<a-tag># {{ item.tag }}</a-tag>
<span class="number">{{ item.count }}</span>
</template>
</list-card>
<list-card :items="suggestScholars" class="side-card">
<template slot="title">
<span>可能感兴趣的人</span>
<span>换一批</span>
</template>
<template #content="{ item }">
<div class="scholar">
<div class="name">
<h2>{{ item.name }}</h2>
<a-button v-if="item.followed" shape="round" class="btn">
已关注
</a-button>
<a-button v-else type="primary" shape="round" class="btn">
关注
</a-button>
</div>
<div>研究领域:{{ item.field }}</div>
<div>{{ item.institution }} · {{ item.position }}</div>
</div>
</template>
</list-card>
Vue slot 插槽用法:自定义列表组件的更多相关文章
- 总结Vue第二天:自定义子组件、父子组件通信、插槽
总结Vue第二天:自定义子组件.父子组件通信.插槽 一.组件: 组件目录 1.注册组件(全局组件.局部组件和小demo) 2.组件数据存放 3.父子组件通信(父级向子级传递数据.子级向父级传递数据) ...
- Vue slot插槽
插槽用于内容分发,存在于子组件之中. 插槽作用域 父级组件作用域为父级,子级组件作用域为子级,在哪定义的作用域就在哪. 子组件之间的内容是在父级作用域的,无法直接访问子组件里面的数据. 插槽元素 &l ...
- Vue slot插槽内容分发
slot插槽使用 使用场景,一般父组件中又一大段模板内容需要运用到子组件上.或者更加复杂的,子组件需要运用到父组件大段模板内容,而子组件却不知道挂载的内容是什么.挂载点的内容是由父组件来决定的. Sl ...
- vue slot 插槽详解
插槽含义:就是引入子组件后,在插入子组件元素中添加信息或者标签,使得子组件的指定位置插入信息或者标签 插槽有三种:默认插槽.具名插槽.作用域插槽,由于vue2.6.0后对插槽进行修改,但是兼容2.6. ...
- Vue slot插槽通俗解释
slot内容分发是Vue的Api来源 <div id="app"> <my-list> {{msg}} </my-list> </div& ...
- Vue.js 桌面端自定义滚动条组件|vue美化滚动条VScroll
基于vue.js开发的小巧PC端自定义滚动条组件VScroll. 前段时间有给大家分享一个vue桌面端弹框组件,今天再分享最近开发的一个vue pc端自定义滚动条组件. vscroll 一款基于vue ...
- vue slot插槽的使用
slot插槽的使用场景 父组件向子组件传递dom时会用到插槽 作用域插槽:当同一个子组件想要在不同的父组件里展示不同的状态,可以使用作用域插槽.展示的状态由父组件来决定 注:想要修改父组件向子 ...
- vue slot插槽的使用方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- vue slot 插槽备忘
老是记不住插槽咋回事 记录下来备忘 父组件 <tab><template slot="boy" slot-scope="test">{{ ...
随机推荐
- 数据权限筛选(RLS)的两种实现介绍
在应用程序中,尤其是在统计的时候, 需要使用数据权限来筛选数据行. 简单的说,张三看张三部门的数据, 李四看李四部门的数据:或者员工只能看自己的数据, 经理可以看部门的数据.这个在微软的文档中叫Row ...
- 全面解析Pytorch框架下模型存储,加载以及冻结
最近在做试验中遇到了一些深度网络模型加载以及存储的问题,因此整理了一份比较全面的在 PyTorch 框架下有关模型的问题.首先咱们先定义一个网络来进行后续的分析: 1.本文通用的网络模型 import ...
- ES6 学习笔记之对象的拓展
1.属性的简洁表示法 ES6 允许直接写入变量和函数,作为对象的属性和方法.这样书写更加简洁. const foo = 'bar'; const baz = {foo}; baz //{foo: &q ...
- Java 读取Word表格中的文本和图片
本文通过Java程序来展示如何读取Word表格,包括读取表格中的文本和图片.下面是具体实现的步骤和方法. 1. 程序环境准备 代码编译工具:IntelliJ IDEA Jdk版本:1.8.0 测试文档 ...
- Redis之复制
1.配置 1.1 建立复制 参与复制的Redis实例划分为主节点(master) 和从节点(slave).默认情况下,Redis都是主节点.每个从节点只能有一个主节点,而主节点可以同时具有多个从节点. ...
- Mybatis:Mybatis 逆向工程 generator配置
一.使用Maven方式引入Mybatis依赖Jar包(版本号自己改或定义)
- 创建者模式 -- 单例模式(反射&序列化)
看的视频:https://www.bilibili.com/video/av43896218/?p=286 实现方式:(构造器私有,提供一个外部可以访问的方法(可以提供实例)) 1.饿汉式:线程安全, ...
- 滑动窗口通用解leetcode字符串匹配问题
滑动窗口,这玩意解决一些字符串匹配的题目是真的挺好用的,虽然本质还是双指针. 思路: 1.维护一个窗口,不断的向右边移动 2.满足要求后,移动左边,当不满足时,跳出. 3.重复1,2.得出答案. 下面 ...
- Acunetix敏感的数据泄露–泄露如何发生
术语"敏感数据暴露"是指允许未授权方访问存储或传输的敏感信息,例如信用卡号或密码.全球范围内大多数重大安全漏洞都会导致某种敏感的数据泄露. Acunetix利用攻击漏洞(例如Web ...
- MyBatis框架中的条件查询!关键字exists用法的详细解析
exists用法 exists: 如果括号内子查询语句返回结果不为空,说明where条件成立,就会执行主SQL语句 如果括号内子查询语句返回结果为空,说明where条件不成立,就不会执行主SQL语句 ...