在编写vue通用组件
时经常会遇到子组件的插槽需从父组件传递过来,并还可以给插槽绑定数据,如ant-design-vue中的下拉菜单多选项时可以通过插槽自定义tag的内容
先展示下效果:
组件默认效果:
使用了tag插槽的效果:
1、思路
核心思路是:将父组件中的插槽以参数的形式传递给子孙组件
,而不是使用插槽传递,如<template #xxx><slot name="xxx"></slot></template>
这里有两种方式可以将父组件的插槽以参数形式传递给子孙组件:
provide
+inject
形式- 子组件定义
props
,父组件将slots
传递给子组件
这两种方式各有千秋,看自己喜欢了
2、编码:使用 provide
+inject
方式实现
父组件
<template>
<div class="input-tags">
<input type="text" class="input-tags-inputer">
<div class="input-tags-list">
<MyTag
v-for="tag in tags"
:key="tag.value"
:tag-data="tag"></MyTag>
</div>
</div>
</template>
<script>
import {
provide
} from 'vue';
import MyTag from './MyTag.vue';
export default {
name: 'InputTags',
props: {
tags: {
type: Array,
default () {
return [];
}
}
},
components: {
MyTag
},
setup (props, ctx) {
provide('inputTagsCtx', ctx);
return {};
}
};
</script>
<style lang="scss">
.input-tags{
//display: inline-block;
position: relative;
}
.input-tags-inputer{
display: block;
width: 100%;
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
&:focus{
color: #495057;
background-color: #fff;
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgb(0 123 255 / 25%);
}
}
.input-tags-list{
position: absolute;
top: 1px;
left: 1px;
bottom: 1px;
right: 1px;
padding: 0.25rem 0 0 0.5rem;
.my-tag{
margin-right: 0.5rem;
}
}
</style>
子组件
<template>
<div class="my-tag">
<MyTagContent :tag-data="tagData">
{{ tagData.label }}
</MyTagContent>
</div>
</template>
<script>
import { MyTagContent } from './MyTagContent';
export default {
name: 'MyTag',
components: {
MyTagContent
},
props: {
tagData: {
type: [Object],
default () {
return {};
}
}
}
};
</script>
<style lang="scss">
.my-tag{
display: inline-block;
vertical-align: middle;
height: 1.75rem;
line-height: calc(1.75rem - 2px);
max-width: 100%;
padding: 0 0.5rem;
border: 1px solid #d9ecff;
border-radius: 0.25rem;
background-color: #ecf5ff;
color: #007bff;
font-size: 0.75rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: width .3s, height .3s, font-size .3s, transform .3s;
}
</style>
tag内容组件(如果用jsx可以省略这个组件)
import {
inject
} from 'vue';
export function MyTagContent (props, ctx) {
let slot;
// 获取父级组件传递过来的上下文
let inputTagsCtx = inject('inputTagsCtx');
if (inputTagsCtx && inputTagsCtx.slots.tag) {
// 如果父组件传递了tag插槽则优先使用父组件传递的
slot = inputTagsCtx.slots.tag;
} else {
slot = ctx.slots.default;
}
return slot(props.tagData);
};
MyTagContent.props = ['tagData'];
组件调用
<template>
<div class="box">
<h3>默认的tag效果</h3>
<InputTags
:tags="[
{ label: 'Html', value: 'Html' },
{ label: 'Javascript', value: 'Javascript' },
{ label: 'Css', value: 'Css' }
]">
</InputTags>
<h3 style="margin-top: 1rem;">自定义tag</h3>
<InputTags
:tags="[
{ label: 'Html', value: 'Html' },
{ label: 'Javascript', value: 'Javascript' },
{ label: 'Css', value: 'Css' }
]">
<template #tag="tag">
标签名:<strong style="font-size: 1.2em;">{{ tag.label }}</strong>
</template>
</InputTags>
</div>
</template>
3、编码:使用 子组件定义props
方式实现
父组件
<template>
<div class="input-tags">
<input type="text" class="input-tags-inputer">
<div class="input-tags-list">
<MyTag
v-for="tag in tags"
:key="tag.value"
:tag-data="tag"
:parent-slots="$slots"></MyTag>
</div>
</div>
</template>
<script>
import MyTag from './MyTag.vue';
export default {
name: 'InputTags',
props: {
tags: {
type: Array,
default () {
return [];
}
}
},
components: {
MyTag
}
};
</script>
<style lang="scss">
.input-tags{
//display: inline-block;
position: relative;
}
.input-tags-inputer{
display: block;
width: 100%;
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
&:focus{
color: #495057;
background-color: #fff;
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgb(0 123 255 / 25%);
}
}
.input-tags-list{
position: absolute;
top: 1px;
left: 1px;
bottom: 1px;
right: 1px;
padding: 0.25rem 0 0 0.5rem;
.my-tag{
margin-right: 0.5rem;
}
}
</style>
子组件
<template>
<div class="my-tag">
<MyTagContent :parent-slots="parentSlots" :tag-data="tagData">
{{ tagData.label }}
</MyTagContent>
</div>
</template>
<script>
import { MyTagContent } from './MyTagContent';
export default {
name: 'MyTag',
components: {
MyTagContent
},
props: {
tagData: {
type: [Object],
default () {
return {};
}
},
parentSlots: { // 父组件的插槽
type: [Object],
default () {
return {};
}
}
}
};
</script>
<style lang="scss">
.my-tag{
display: inline-block;
vertical-align: middle;
height: 1.75rem;
line-height: calc(1.75rem - 2px);
max-width: 100%;
padding: 0 0.5rem;
border: 1px solid #d9ecff;
border-radius: 0.25rem;
background-color: #ecf5ff;
color: #007bff;
font-size: 0.75rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: width .3s, height .3s, font-size .3s, transform .3s;
}
</style>
tag内容组件(如果用jsx可以省略这个组件)
export function MyTagContent (props, ctx) {
let slot;
if (props.parentSlots.tag) {
// 如果父组件传递了tag插槽则优先使用父组件传递的
slot = props.parentSlots.tag;
} else {
slot = ctx.slots.default;
}
return slot(props.tagData);
};
MyTagContent.props = ['tagData', 'parentSlots'];
组件调用
<template>
<div class="box">
<h3>默认的tag效果</h3>
<InputTags
:tags="[
{ label: 'Html', value: 'Html' },
{ label: 'Javascript', value: 'Javascript' },
{ label: 'Css', value: 'Css' }
]">
</InputTags>
<h3 style="margin-top: 1rem;">自定义tag</h3>
<InputTags
:tags="[
{ label: 'Html', value: 'Html' },
{ label: 'Javascript', value: 'Javascript' },
{ label: 'Css', value: 'Css' }
]">
<template #tag="tag">
标签名:<strong style="font-size: 1.2em;">{{ tag.label }}</strong>
</template>
</InputTags>
</div>
</template>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。