Generic компоненты — это компоненты, способные работать с различными типами данных или структур без изменения внутренней логики. Их основное назначение — повышение переиспользуемости кода и снижение дублирования. В Vue.js такой подход реализуется через слоты, пропсы с типами и композицию компонентов.
Слоты позволяют вставлять произвольный контент в компонент.
Стандартный слот обеспечивает максимальную гибкость, а именованные слоты
и слот v-slot добавляют возможность детальной
настройки.
<template>
<div class="card">
<header>
<slot name="header">Заголовок по умолчанию</slot>
</header>
<main>
<slot>Контент по умолчанию</slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script>
export default {
name: 'CardComponent'
}
</script>
Такой компонент может использоваться с любым содержимым, превращаясь в generic-структуру для карточек.
<CardComponent>
<template #header>
<h1>Новости</h1>
</template>
<p>Основной контент карточки.</p>
<template #footer>
<small>Источник: Vue.js Docs</small>
</template>
</CardComponent>
Generic компоненты часто используют обобщенные
пропсы для передачи данных с различной структурой. Vue 3
поддерживает строгую типизацию пропсов через TypeScript или через объект
props с указанием типа.
<script lang="ts">
import { defineComponent, PropType } from 'vue'
interface Item {
id: number
name: string
}
export default defineComponent({
name: 'ListComponent',
props: {
items: {
type: Array as PropType<Item[]>,
required: true
}
}
})
</script>
В шаблоне можно динамически рендерить элементы любого типа, используя
v-for:
<template>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
Преимущество такого подхода — компонент становится независимым от конкретного набора данных и может работать с любыми массивами объектов, соответствующими интерфейсу.
Generic компонент может принимать рендер-функцию или метод для кастомного рендера каждого элемента. Такой паттерн называют render prop.
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item">{{ item.name }}</slot>
</li>
</ul>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue'
interface Item {
id: number
name: string
}
export default defineComponent({
name: 'CustomList',
props: {
items: {
type: Array as PropType<Item[]>,
required: true
}
}
})
</script>
Использование компонента с кастомным рендером:
<CustomList :items="users">
<template #default="{ item }">
<strong>{{ item.name }}</strong> (ID: {{ item.id }})
</template>
</CustomList>
Такой подход делает компонент полностью generic и расширяемым без изменения его внутреннего кода.
isДля построения generic интерфейсов с различными типами визуальных
компонентов используют динамические компоненты с атрибутом
:is.
<component :is="currentComponent" :data="data" />
Здесь currentComponent может быть любым
зарегистрированным компонентом, а data передается
одинаковым образом. Это позволяет строить универсальные
контейнеры, способные рендерить различные типы контента.
Composition API усиливает generic подход, предоставляя возможность вынести повторяющуюся логику в composable функции. Generic компонент может использовать такие функции для работы с данными любого типа:
import { ref } from 'vue'
export function useGenericList<T>(initialItems: T[]) {
const items = ref<T[]>(initialItems)
const addItem = (item: T) => items.value.push(item)
return { items, addItem }
}
Использование в компоненте:
import { defineComponent } from 'vue'
import { useGenericList } from './useGenericList'
export default defineComponent({
setup() {
const { items, addItem } = useGenericList<{ id: number; name: string }>([])
return { items, addItem }
}
})
Такой компонент становится максимально абстрактным и пригодным для любых типов данных, соответствующих ожидаемой структуре.
Generic компоненты в Vue.js обеспечивают высокий уровень переиспользуемости и гибкости. Слоты, типизированные пропсы, рендер-пропсы, динамические компоненты и Composition API — ключевые инструменты для построения абстрактных, независимых компонентов, способных работать с разнообразными данными и визуальными шаблонами.