Svelte中条件渲染默认插槽的存在性检测问题
Svelte条件渲染默认插槽时,无法正确检测插槽存在状态的解决办法
问题场景
我开发了一个带默认插槽和具名插槽的Svelte组件,在父组件使用时,把默认插槽的内容放在了{#if checked}条件语句里。但子组件里用Object.keys($$slots).includes('default')判断默认插槽是否存在时,哪怕checked为false、条件块根本不渲染内容,这个判断还是会返回true。结果就是组件始终渲染默认插槽对应的内边距和边框,把布局搞乱了。
子组件检测代码:
const hasDefaultSlot = Object.keys($$slots).includes('default');
父组件使用代码:
<MyComponent> <div slot="header"> header </div> {#if checked} <div>body</div> {/if} </MyComponent>
原因
Svelte的$$slots检测的是插槽有没有被父组件声明使用,而不是插槽内容是否实际渲染。只要父组件里写了默认插槽的位置(哪怕被条件语句包裹),编译器就会认定default插槽存在,$$slots里就会包含这个键,不管内容会不会真的显示出来。
解决方案
方案1:父组件直接传递状态给子组件(最推荐)
既然checked决定了默认插槽内容是否显示,直接把这个变量传给子组件,子组件根据它来控制样式和布局,不用再检测插槽。
父组件修改:
<MyComponent {checked}> <div slot="header"> header </div> {#if checked} <div>body</div> {/if} </MyComponent>
子组件修改:
export let checked; // 用checked代替hasDefaultSlot来判断是否渲染边框、内边距
这种方法简单直接,完全符合Svelte的数据流逻辑,没有额外的复杂度。
方案2:子组件检测插槽是否有实际渲染内容
如果没法直接传递checked状态,可以通过DOM元素来判断插槽是否真的有内容渲染。在子组件里给插槽容器绑定this,然后检测容器内是否有子元素:
子组件代码:
<!-- 给插槽容器绑定ref --> <div bind:this={defaultSlotWrapper} class="default-slot-area"> {#if $$slots.default} <slot /> {/if} </div> <script> import { onMount, onUpdate } from 'svelte'; let defaultSlotWrapper; let hasDefaultContent = false; // 检测插槽容器是否有子元素 function checkSlotContent() { hasDefaultContent = defaultSlotWrapper?.children.length > 0; } // 挂载和更新时都检测一次 onMount(checkSlotContent); onUpdate(checkSlotContent); </script> <style> /* 根据hasDefaultContent控制样式 */ .default-slot-area { padding: {hasDefaultContent ? '1rem' : '0'}; border: {hasDefaultContent ? '1px solid #eee' : 'none'}; } </style>
注意:如果插槽内容是异步加载的(比如请求数据后渲染),可能需要额外监听内容变化,比如用MutationObserver来检测DOM更新。
内容的提问来源于stack exchange,提问作者Justin Golden
相关产品推荐
相关产品推荐

