本节目标
- 默认插槽
- 后备内容
- 具名插槽
- 作用域插槽
- 案例-商品列表
默认插槽
让组件内部的一些结构 支持自定义
步骤
- 组件内需要定制的结构, 使用<slot></slot>占位
- 使用组件时, 传入结构替换slot的位置
后备内容
封装组件时, 可以为预留的<slot>插槽提供默认内容
步骤
- 在<slot>标签内, 放置内容, 作为默认显示内容
- 使用组件时, 如果不传东西, 显示slot后备内容
- 使用组件时, 传递内容, 则slot整体会被替换掉
具名插槽
一个组件内有多处结构, 需要外部传入标签, 进行定制, 需要使用具名插槽
步骤
- 组件内的slot标签, 使用 name属性 给插槽命名
- 使用组件时, 使用 template 配合 v-slot:插槽名 来分发标签
- v-slot:插槽名 可以简写成 #插槽名
作用域插槽
定义slot插槽的同时, 是可以传值的, 给插槽上绑定数据, 使用组件时可以接收数据
组件分类
- 默认插槽(组件内定义一处结构)
- 具名插槽(组件内定义多处结构)
- 作用域插槽不是一类组件, 是插槽的传参语法
封装表格组件
- 父传子, 动态渲染表格内容
- 利用默认插槽, 定制操作列
- 删除或查看操作, 都需要用到当前项的id, 属于组件内部的数据, 可以通过作用域插槽绑定数据, 供外部使用
步骤
- 给slot标签, 以添加属性的方式传值
- 所有添加的属性, 都会被收集到一个对象中
- 在template中, 通过 #插槽名="obj" 接收数据, 默认插槽, 通过 #default="obj" 接收数据
案例-商品列表
1>分析需求
my-ta组件
- 双击显示输入框, 输入框自动获取焦点
- 失去焦点, 隐藏输入框
- 回显标签信息
- 内容修改, 回车, 修改标签信息
my-table组件
- 动态传递表格数据渲染
- 表头支持自定义
- 主体支持自定义
2>创建tag组件
<template>
<div class="my-tag">
<!-- <input
class="input"
type="text"
placeholder="输入标签"
/> -->
<div class="text">茶具</div>
</div>
</template>
<script>
export default {
};
</script>
<style lang="less" scoped>
.my-tag {
cursor: pointer;
.input {
appearance: none;
outline: none;
border: 1px solid #ccc;
width: 100px;
height: 40px;
box-sizing: border-box;
padding: 10px;
color: #666;
&::placeholder {
color: #666;
}
}
}
</style>
3>控制显示隐藏
<template>
<div class="my-tag">
<input
v-if="isEdit"
v-focus
class="input"
type="text"
placeholder="输入标签"
@blur="isEdit = false"
/>
<div v-else class="text" @dblclick="handerEdit">茶具</div>
</div>
</template>
<script>
export default {
data() {
return {
isEdit: false,
};
},
methods: {
handerEdit() {
this.isEdit = true;
},
},
};
</script>
... ...
// 全局注册自动获取焦点指令
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
... ...
4>回显/修改数据
<template>
<div class="my-tag">
<input
... ...
:value="value"
@keyup.enter="inputEnter"
/>
... ...
</div>
</template>
<script>
export default {
// 通过v-model完成父子组件通信
props: {
value: String,
},
methods: {
... ...
// 监听键盘回车事件
inputEnter(e) {
// 非空处理
if (e.target.value.trim() === "") return alert("输入不能为空");
// 触发自定义事件, 更新父组件数据
// 通过事件对象拿到输入的新值
this.$emit("input", e.target.value);
// 编辑完成后, 隐藏输入框
this.isEdit = false;
},
},
};
</script>
<template>
<div class="table-case">
<table class="my-table">
... ...
<tbody>
<tr>
<td>1</td>
<td>梨皮朱泥三绝清代小品壶经典款紫砂壶</td>
<td>
<img src="https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg" />
</td>
<td>
<my-tag v-model="text"></my-tag>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'TableCase',
data() {
return {
// 临时绑定数据,检测v-model
text: '默认数据';
}
},
}
</script>
5>创建my-table组件
<template>
<table class="my-table">
<thead>
<tr>
<th>编号</th>
<th>名称</th>
<th>图片</th>
<th width="100px">标签</th>
</tr>
</thead>
<tbody>
<tr v-for="item in data" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<img :src="item.picture" />
</td>
<td>
MyTage组件
<!-- <MyTage v-model="item.tag"></MyTage> -->
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
props: {
data: {
type: Array,
require: true,
},
},
};
</script>
<style lang="less" scoped>
.my-table {
width: 100%;
border-spacing: 0;
img {
width: 100px;
height: 100px;
object-fit: contain;
vertical-align: middle;
}
th {
background: #f5f5f5;
border-bottom: 2px solid #069;
}
td {
border-bottom: 1px dashed #ccc;
}
td,
th {
text-align: center;
padding: 10px;
transition: all 0.5s;
&.red {
color: red;
}
}
.none {
height: 100px;
line-height: 100px;
color: #999;
}
}
</style>
<template>
<div class="table-case">
<MyTable :data="goods"></MyTable>
</div>
</template>
<script>
export default {
name: "TableCase",
data() {
return {
goods: [
{
id: 101,
picture:
"https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg",
name: "梨皮朱泥三绝清代小品壶经典款紫砂壶",
tag: "茶具",
},
{
id: 102,
picture:
"https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg",
name: "全防水HABU旋钮牛皮户外徒步鞋山宁泰抗菌",
tag: "男鞋",
},
{
id: 103,
picture:
"https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png",
name: "毛茸茸小熊出没,儿童羊羔绒背心73-90cm",
tag: "儿童服饰",
},
{
id: 104,
picture:
"https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg",
name: "基础百搭,儿童套头针织毛衣1-9岁",
tag: "儿童服饰",
},
],
};
},
};
</script>
<style lang="less" scoped>
.table-case {
width: 1000px;
margin: 50px auto;
img {
width: 100px;
height: 100px;
object-fit: contain;
vertical-align: middle;
}
}
</style>
6>结构自定义
<template>
<table class="my-table">
<thead>
<tr>
<slot name="header"></slot>
</tr>
</thead>
<tbody>
<tr v-for="item in data" :key="item.id">
<slot name="body" :item="item"></slot>
</tr>
</tbody>
</table>
</template>
<script>
export default {
props: {
data: {
type: Array,
require: true,
},
},
};
</script>
<template>
<div class="table-case">
<MyTable :data="goods">
<template #header>
<th>编号</th>
<th>名称</th>
<th>图片</th>
<th width="100px">标签</th>
</template>
<template #body="{ item }">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<img :src="item.picture" />
</td>
<td>
<!-- MyTage组件 -->
<MyTage v-model="item.tag"></MyTage>
</td>
</template>
</MyTable>
</div>
</template>
<script>
export default {
name: "TableCase",
data() {
return {
goods: [
{
id: 101,
picture:
"https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg",
name: "梨皮朱泥三绝清代小品壶经典款紫砂壶",
tag: "茶具",
},
{
id: 102,
picture:
"https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg",
name: "全防水HABU旋钮牛皮户外徒步鞋山宁泰抗菌",
tag: "男鞋",
},
{
id: 103,
picture:
"https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png",
name: "毛茸茸小熊出没,儿童羊羔绒背心73-90cm",
tag: "儿童服饰",
},
{
id: 104,
picture:
"https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg",
name: "基础百搭,儿童套头针织毛衣1-9岁",
tag: "儿童服饰",
},
],
};
},
};
</script>