svelte:component 有 DOM 个元素
svelte:component with DOM elements
目标
我正在 Svelte 中创建一个按钮组件,它将呈现为 <button>
或 <a>
元素,具体取决于它是否为 link。是否可以使用 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
动机
可以使用if/else来获得想要的结果……
<script lang='ts'>
export let href: string = ''
</script>
{# if href}
<a {href}>
<slot></slot>
</a>
{:else}
<button>
<slot></slot>
</button>
{/if}
…但是这是不可维护的。
- 全部内容重复。在上面的简单示例中,内容只是子项。实际上,有多个槽(例如,prefixes/suffixes),导致大量重复逻辑。
- 我可以在具有 2 个以上变体的许多其他组件中看到此设计模式的用途(例如,
Container
组件可以是 div
、section
、article
、aside
等)。更多变体导致代码结构更加混乱。
React 等价物
这是一个具有所需功能的示例 React 组件。
const Button = (props) => {
const Tag = props.href ? 'a' : 'button'
return <Tag href={props.href}>{contents}</Tag>
}
我希望避免的解决方案
1。 if/else 模式
与上面相同的模式。
2。创建一个“子”组件
您可以将其移动到自己的组件中,然后将子项导入 if/else 链,而不是多次复制子项。至少这样不会有重复的逻辑,但是 props/slots 需要重复。
<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:component>
。有一个proposal to add a new <svelte:element>
tag for this functionality, but it has not been implemented yet. There is an open PR将这个标签添加到Svelte。
编辑:现在可以在 Svelte 3.47.0 中使用 svelte:element。您现在可以执行以下操作:
<script>
export let href = '';
let tag = href ? 'a' : 'button';
</script>
<svelte:element this={tag} {href}>
<slot></slot>
</svelte:element>
目标
我正在 Svelte 中创建一个按钮组件,它将呈现为 <button>
或 <a>
元素,具体取决于它是否为 link。是否可以使用 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
动机
可以使用if/else来获得想要的结果……
<script lang='ts'>
export let href: string = ''
</script>
{# if href}
<a {href}>
<slot></slot>
</a>
{:else}
<button>
<slot></slot>
</button>
{/if}
…但是这是不可维护的。
- 全部内容重复。在上面的简单示例中,内容只是子项。实际上,有多个槽(例如,prefixes/suffixes),导致大量重复逻辑。
- 我可以在具有 2 个以上变体的许多其他组件中看到此设计模式的用途(例如,
Container
组件可以是div
、section
、article
、aside
等)。更多变体导致代码结构更加混乱。
React 等价物
这是一个具有所需功能的示例 React 组件。
const Button = (props) => {
const Tag = props.href ? 'a' : 'button'
return <Tag href={props.href}>{contents}</Tag>
}
我希望避免的解决方案
1。 if/else 模式
与上面相同的模式。
2。创建一个“子”组件
您可以将其移动到自己的组件中,然后将子项导入 if/else 链,而不是多次复制子项。至少这样不会有重复的逻辑,但是 props/slots 需要重复。
<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:component>
。有一个proposal to add a new <svelte:element>
tag for this functionality, but it has not been implemented yet. There is an open PR将这个标签添加到Svelte。
编辑:现在可以在 Svelte 3.47.0 中使用 svelte:element。您现在可以执行以下操作:
<script>
export let href = '';
let tag = href ? 'a' : 'button';
</script>
<svelte:element this={tag} {href}>
<slot></slot>
</svelte:element>