Полиморфные компоненты в SvelteKit представляют собой универсальные компоненты, способные рендериться с разными HTML- или пользовательскими тегами в зависимости от переданных пропсов. Основная цель полиморфизма — повышение переиспользуемости кода и упрощение управления стилизацией и поведением компонентов без создания множества специализированных версий одного и того же элемента.
В JavaScript-библиотеках UI, таких как svelte-ui или
shadcn-svelte, полиморфные компоненты часто используют
проп as, который определяет тег или
компонент, в котором будет осуществляться рендеринг.
<script lang="ts">
export let as: string = 'div';
</script>
<svelte:element this={as}>
<slot />
</svelte:element>
В этом примере <svelte:element> — это ключевой
инструмент для создания полиморфного компонента в SvelteKit. Проп
as позволяет динамически подставлять любой
HTML-элемент.
asИспользование пропа as делает компонент гибким для
разных контекстов:
<Button as="a" href="/home">Перейти на главную</Button>
<Button as="button" on:click={handleClick}>Нажми меня</Button>
<a> с ссылкой.Такой подход устраняет дублирование кода, когда один
и тот же компонент должен вести себя как <button> или
<a>.
Полиморфные компоненты сохраняют унифицированный стиль, независимо от того, какой тег используется. Это достигается через объединение классов:
<script lang="ts">
export let className: string = '';
export let as: string = 'div';
</script>
<svelte:element this={as} class={`base-styles ${className}`}>
<slot />
</svelte:element>
Здесь base-styles задают общий дизайн, а
className позволяет добавлять специфичные стили. Такой
подход особенно полезен для библиотек UI, где компоненты должны
выглядеть одинаково на разных элементах.
В TypeScript важно корректно типизировать проп as и
сопутствующие свойства. Простейший способ — использование дженериков и
условной типизации:
type PolymorphicProps<T extends keyof HTMLElementTagNameMap> = {
as?: T;
} & Omit<svelte.JSX.HTMLAttributes<HTMLElementTagNameMap[T]>, 'as'>;
Пример использования:
<script lang="ts">
import type { PolymorphicProps } from './types';
export let as: 'a' | 'button' = 'button';
export let href?: string;
</script>
<svelte:element this={as} href={href}>
<slot />
</svelte:element>
Таким образом, редактор поддерживает автодополнение и проверку типов, предотвращая ошибки передачи неподходящих атрибутов для конкретного тега.
Полиморфные компоненты не ограничиваются HTML-элементами. Они могут оборачивать и другие компоненты, передавая им пропсы:
<script lang="ts">
export let as: typeof CustomCard | 'div' = 'div';
</script>
<svelte:component this={as}>
<slot />
</svelte:component>
<svelte:component> используется для динамического
рендеринга компонентов. Такой подход позволяет создавать
универсальные UI-обёртки, которые могут рендерить как
стандартный элемент, так и кастомный компонент библиотеки.
Для полноценной гибкости важно корректно передавать все атрибуты, включая обработчики событий и классы:
<script lang="ts">
export let as: string = 'div';
export let props: Record<string, any> = {};
</script>
<svelte:element this={as} {...props}>
<slot />
</svelte:element>
Пример использования:
<Polymorphic as="button" props={{ onClick: handleClick, class: 'btn-primary' }}>
Нажми меня
</Polymorphic>
Такой подход делает компонент максимально универсальным и расширяемым.
Полиморфные компоненты идеально сочетаются с утилитарными классами:
<Button as="a" class="text-white bg-blue-500 px-4 py-2 rounded">
Перейти
</Button>
as="a" задаёт тег <a>.Полиморфные компоненты могут создавать лишние уровни рендера, если использовать их слишком глубоко в дереве. Для оптимизации рекомендуется:
<svelte:element> и
<svelte:component>.as только там, где это действительно
необходимо.slot для передачи содержимого, а не
создавать дополнительные компоненты для каждого варианта тега.<article> или <div>.<p>,
<span>, <strong> с едиными
стилями.Использование этих паттернов обеспечивает масштабируемость UI-библиотеки, облегчает поддержку и делает компоненты гибкими для любых задач.
Полиморфные компоненты являются фундаментальным инструментом при создании универсальных, переиспользуемых и типизированных компонентов в SvelteKit UI-библиотеках.