【发布时间】:2021-07-20 04:05:54
【问题描述】:
目标
我正在 Svelte 中创建一个按钮组件,该组件将呈现为 <button> 或 <a> 元素,具体取决于它是否为链接。可以使用svelte:component吗?
这样的……
<script lang='ts'>
export let href: string = ''
$: component = href ? a : button // where "a" and "button" are the HTML DOM elements
</script>
<svelte:component this={component}>
<slot></slot>
</svelte:component>
到目前为止,我只看到了 svelte:component 渲染自定义 Svelte 组件的示例,而不是 DOM 元素
https://svelte.dev/tutorial/svelte-component
How to dynamically render components in Svelte?
动机
可以使用 if/else 来获得想要的结果……
<script lang='ts'>
export let href: string = ''
</script>
{# if href}
<a {href}>
<slot></slot>
</a>
{:else}
<button>
<slot></slot>
</button>
{/if}
...但是这是不可维护的。
- 整个内容被复制。在上面的简单示例中,内容只是子项。实际上,有多个插槽(例如前缀/后缀),导致大量重复逻辑。
- 我可以在许多其他具有 2 个以上变体的组件中看到这种设计模式的用途(例如,
Container组件可以是div、section、article、aside等。 )。变体越多,代码结构就越混乱。
反应等效
这是一个具有所需功能的示例 React 组件。
const Button = (props) => {
const Tag = props.href ? 'a' : 'button'
return <Tag href={props.href}>{contents}</Tag>
}
我希望避免的解决方案
1。 if/else 模式
与上述相同的模式。
2。创建一个“子”组件
您可以将其移动到自己的组件中,然后将子项导入到 if/else 链中,而不是多次复制子项。至少那样不会有重复的逻辑,但道具/插槽将需要重复。
<script lang='ts'>
import Children from './children.svelte'
export let href: string = ''
</script>
{# if href}
<a {href}>
<Children><slot></slot></Children>
</a>
{:else}
<button>
<Children><slot></slot></Children>
</button>
{/if}
3。创建包装器组件
对于每个包装 DOM 元素,只需创建一个新的 Svelte 组件。然后,将它们作为实际的 Svelte 组件导入并使用 svelte:component
<!-- dom-a.svelte -->
<a {...$$props}><slot></slot></a>
<!-- dom-button.svelte -->
<button {...$$props}><slot></slot></button>
<!-- button.svelte -->
<script lang='ts'>
import A from './dom-a.svelte'
import Button from './dom-button.svelte'
export let href: string = ''
$: component = href ? A : Button
</script>
<svelte:component this={component}>
<slot></slot>
</svelte:component>
虽然这是最适合作为开发人员使用的工具,但有一个 performance penalty 用于拥有未知道具。因此,这不是想法。
我想你可以在 dom-button.svelte 和 dom-a.svelte 组件中指定每一个可能的道具,但这似乎有点矫枉过正。
【问题讨论】:
标签: svelte svelte-3 svelte-component