在编写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>