svelte: 带有 DOM 元素的组件 [英] svelte:component with DOM elements
问题描述
我正在 Svelte 中创建一个按钮组件,它将呈现为 或
元素,具体取决于它是否是链接.是否可以使用
svelte:component
?
I'm creating a button component in Svelte that will either render as <button>
or <a>
element, depending on whether it's a link or not. Is it possible to use svelte:component
?
这样的事情......
Something like this…
<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 元素
So far, I've only seen examples of svelte:component
rendering custom Svelte components, not DOM elements
https://svelte.dev/tutorial/svelte-component
可以使用 if/else 来获得想要的结果...
It is possible to use if/else to get the desired results…
<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
、文章
、aside
等).更多的变体导致代码结构更加混乱.
- The entire contents is duplicated. In the simple example above, the contents is just the children. In reality, there are multiple slots for (e.g, prefixes/suffixes), leading to lots of duplicated logic.
- I can see uses for this design pattern in many other components with more than 2 variants (e.g., a
Container
component that can be adiv
,section
,article
,aside
, etc.). More variants results in an even messier code structure.
这是具有所需功能的示例 React 组件.
Here's an example React component with the desired functionality.
const Button = (props) => {
const Tag = props.href ? 'a' : 'button'
return <Tag href={props.href}>{contents}</Tag>
}
我希望避免的解决方案
1.if/else 模式
与上面相同的模式.
Solutions I wish to avoid
1. The if/else pattern
Same pattern as above.
与其多次复制子项,您可以将其移动到自己的组件中,然后将子项导入 if/else 链.至少这样不会有重复的逻辑,但是道具/插槽需要重复.
Instead of duplicating the children multiple times, you could move it into its own component and just import the child into the if/else chain. At least then there won't be duplicated logic, but the props/slots will need to be duplicated.
<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>
虽然这是作为开发人员最好的使用方法,但存在一个性能损失道具.因此,这不是想法.
While this is the nicest to use as a developer, there is a performance penalty for having unknown props. Therefore, it's not idea.
我想你可以在 dom-button.svelte
和 dom-a.svelte
组件中指定每一个可能的道具,但这似乎有点矫枉过正.
I suppose you could specify every single possible prop in the dom-button.svelte
and dom-a.svelte
components, but that seems like overkill.
推荐答案
不能使用
来解决这个问题.有一个提案来添加一个新的
It is not possible to use <svelte:component>
for this. There is a proposal to add a new <svelte:element>
tag for this functionality, but it has not been implemented yet. There is an open PR to add this tag to Svelte.
实施后,您将能够执行以下操作:
Once it's implemented, you'll be able to do the following:
<script lang='ts'>
export let href: string = ''
$: tagName = href ? 'a' : 'button';
</script>
<svelte:element tag={tagName}>
<slot></slot>
</svelte:element>
在那之前,解决方法如您在问题中所述.
Until then, the workarounds are as you described in your question.
这篇关于svelte: 带有 DOM 元素的组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!