如果它有任何内容,有没有办法只显示一个插槽?
例如,我正在构建一个简单的 Card.vue
组件,并且我只希望在页脚槽有内容时显示页脚:
模板
<template>
<div class="panel" :class="panelType">
<div class="panel-heading">
<h3 class="panel-title">
<slot name="title">
Default Title
</slot>
</h3>
</div>
<div class="panel-body">
<slot name="body"></slot>
<p class="category">
<slot name="category"></slot>
</p>
</div>
<div class="panel-footer" v-if="hasFooterSlot">
<slot name="footer"></slot>
</div>
</div>
</template>
脚本
<script>
export default {
props: {
active: true,
type: {
type: String,
default: 'default',
},
},
computed: {
panelType() {
return `panel-${this.type}`;
},
hasFooterSlot() {
return this.$slots['footer']
}
}
}
</script>
在视图中:
<card type="success"></card>
由于上述组件不包含页脚,因此不应渲染它,但它是。
我试过使用 this.$slots['footer']
,但这返回未定义。
有没有人有任何提示?
您应该检查 vm.$slots
和 vm.$scopedSlots
。
hasSlot (name = 'default') {
return !!this.$slots[ name ] || !!this.$scopedSlots[ name ];
}
this.$scopedSlots[ name ]
返回一个渲染函数,而不是像 vm.$slots
那样的 vNode 数组。
this.$slots
是一个空对象,this.$scopedSlots
帮助我。
$scopeSlots[name]
是一个函数的答案(Vue 2.1.0+)
CSS 大大简化了这一点。只需使用以下代码,瞧!
.panel-footer:empty {
display: none;
}
这是 Vue 3 组合 API 的解决方案:
<template>
<div class="md:grid md:grid-cols-5 md:gap-6">
<!-- Here, you hide the wrapper if there is no used slot or empty -->
<div class="md:col-span-2" v-if="hasTitle">
<slot name="title"></slot>
</div>
<div class="mt-5 md:mt-0"
:class="{'md:col-span-3': hasTitle, 'md:col-span-5': !hasTitle}">
<div class="bg-white rounded-md shadow">
<div class="py-7">
<slot></slot>
</div>
</div>
</div>
</div>
</template>
<script>
import {ref} from "vue";
export default {
setup(props, {slots}) {
const hasTitle = ref(false)
// Check if the slot exists by name and has content.
// It returns an empty array if it's empty.
if (slots.title && slots.title().length) {
hasTitle.value = true
}
return {
hasTitle
}
}
}
</script>
简而言之,内联执行此操作:
<template lang="pug">
div
h2(v-if="$slots.title")
slot(name="title")
h3(v-if="$slots['sub-title']")
slot(name="sub-title")
</template>
我遇到了一个类似的问题,但是在广泛的代码库中,当创建原子设计结构化组件时,一直编写 hasSlot()
方法可能会很累,而当涉及到 TDD 时——它的另一种测试方法......说,您始终可以将原始逻辑放在 v-if
中,但我发现模板有时会变得混乱且难以阅读,尤其是对于检查代码结构的新开发人员而言。
我的任务是找出在未提供插槽时删除父插槽 div
的方法。
问题:
如您所见,问题是我有一个几乎“尾随”的 div,当组件作者认为不需要
当然,我们可以使用
解决方案
我的解决方案是制作一个通用
//slot 组件
我在尝试这个想法时遇到了用例问题,有时我的标记结构需要为了这种方法的利益而改变。这种方法减少了在每个组件模板中进行小槽检查的需要。我想您可以将该组件视为
还值得注意的是,将属性应用到
希望这可以帮助。
更新我为此编写了一个插件,因此不再需要在每个使用者组件中导入
const SLOT_COMPONENT = { name: 'custom-slot', template: `
bar
插槽时,它可能会提供样式问题。
<div v-if="$slots.bar">...</div>
或 <div v-if="hasBar()">...</div>
等,但就像我说的那样 - 这会让人厌烦并最终变得更难阅读。
slot
组件,它只渲染出一个带有周围 div 的插槽...见下文。
<conditional-div />
组件...
slot-component
实例化 (<slot-component class="myClass" data-random="randomshjhsa" />
) 很好,因为属性会滴入 slot-component
模板的包含 div。
custom-slot
组件,您只需在 main.js 中编写 Vue.use(SlotPlugin)实例化。 (见下文)
最初我认为 https://stackoverflow.com/a/50096300/752916 正在工作,但我不得不对其进行一些扩展,因为 $scopeSlots 返回一个无论其返回值如何总是真实的函数。这是我的解决方案,尽管我得出的结论是,这个问题的真正答案是“这样做是一种反模式,你应该尽可能避免它”。例如,只需制作一个可以插入的单独页脚组件。
哈克解决方案
hasFooterSlot() {
const ss = this.$scopedSlots;
const footerNodes = ss && ss.footer && ss.footer();
return footerNodes && footerNodes.length;
}
最佳实践(页脚的辅助组件)
const panelComponent = { template: `
希望我理解这一点。如果插槽为空,为什么不使用未呈现的 <template>
标记。
<slot name="foo"></slot>
像这样使用它:
<template slot="foo">
...
</template>
经过测试
所以这在 vue 3 中对我有用:
我使用 onMounted 首先获取值,然后使用 onUpdate 来更新值。
<template>
<div v-if="content" class="w-1/2">
<slot name="content"></slot>
</div>
</template>
<script>
import { ref, onMounted, defineComponent, onUpdated } from "vue";
export default defineComponent({
setup(props, { slots }) {
const content = ref()
onMounted(() => {
if (slots.content && slots.content().length) {
content.value = true
}
})
onUpdated(() => {
content.value = slots.content().length
console.log('CHECK VALUE', content.value)
})
})
</script>
@Bert 答案似乎不适用于 <template v-slot:foo="{data}"> ... </template>
等动态模板。我最终使用:
return (
Boolean(this.$slots.foo) ||
Boolean(typeof this.$scopedSlots.foo == 'function')
);
现在,在 Vue3 组合 API 中,您可以使用 useSlots
。
<script setup>
const slots = useSlots()
</script>
<template>
<div v-if="slots.content" class="classname">
<slot name="content"></slot>
</div>
</template>
不定期副业成功案例分享
this.$slots.footer
和this.$slots['footer']
是等价的,不是吗?这真的是解决方案还是有其他问题?v-if
添加回来的元素)this.$slots.footer()