Универсальные компоненты — это повторно используемые блоки интерфейса, которые можно применять в разных местах приложения с минимальными изменениями. Основная цель таких компонентов — снижение дублирования кода и упрощение поддержки проекта.
Для создания универсального компонента необходимо выделить обобщённую логику и шаблон, который может адаптироваться к различным сценариям через props, слоты и события.
Пример универсального компонента кнопки:
<template>
<button
:class="['btn', typeClass, sizeClass]"
:disabled="disabled"
@click="$emit('click')"
>
<slot>Кнопка</slot>
</button>
</template>
<script>
export default {
name: 'BaseButton',
props: {
type: {
type: String,
default: 'primary',
},
size: {
type: String,
default: 'medium',
},
disabled: {
type: Boolean,
default: false,
},
},
computed: {
typeClass() {
return `btn--${this.type}`;
},
sizeClass() {
return `btn--${this.size}`;
},
},
};
</script>
<style scoped>
.btn {
border: none;
cursor: pointer;
}
.btn--primary { background-color: #42b983; color: white; }
.btn--secondary { background-color: #35495e; color: white; }
.btn--medium { padding: 8px 16px; font-size: 14px; }
.btn--large { padding: 12px 24px; font-size: 16px; }
</style>
Ключевые моменты:
Слоты — основной инструмент для повышения универсальности компонентов. Vue поддерживает:
Пример карточки с именованными слотами:
<template>
<div class="card">
<header class="card-header">
<slot name="header"></slot>
</header>
<main class="card-body">
<slot></slot>
</main>
<footer class="card-footer">
<slot name="footer"></slot>
</footer>
</div>
</template>
<script>
export default {
name: 'Card',
};
</script>
<style scoped>
.card { border: 1px solid #ddd; border-radius: 4px; padding: 16px; }
.card-header, .card-footer { font-weight: bold; }
</style>
Использование:
<Card>
<template #header>Заголовок</template>
Основное содержимое карточки
<template #footer>Подвал</template>
</Card>
Vue позволяет переключать компоненты в зависимости от состояния с
помощью component и is:
<component :is="currentComponent"></component>
<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
export default {
data() {
return {
currentComponent: 'ComponentA',
};
},
components: { ComponentA, ComponentB },
};
</script>
Это особенно полезно для универсальных контейнеров, которые должны отображать разные виды содержимого без дублирования шаблонов.
Для повторяющейся логики используют миксины или Composition API.
Пример с Composition API:
import { ref } from 'vue';
export function useToggle(initial = false) {
const state = ref(initial);
const toggle = () => state.value = !state.value;
return { state, toggle };
}
Использование в компоненте:
<script setup>
import { useToggle } from './useToggle';
const { state: isOpen, toggle } = useToggle();
</script>
<template>
<button @click="toggle">Переключить</button>
<p v-if="isOpen">Содержимое открыто</p>
</template>
Универсальные компоненты должны быть чистыми, то есть не хранить лишнего состояния, а управляться через props и события. Это повышает их тестируемость и переиспользуемость.
Пример передачи данных:
<BaseInput v-model="username" placeholder="Введите имя" />
Где v-model автоматически связывает проп
modelValue и событие update:modelValue:
props: { modelValue: String },
emits: ['update:modelValue'],
Для универсальных компонентов важно делегировать события родителю, не
связывая их с конкретной бизнес-логикой. Это достигается через
$emit:
<button @click="$emit('submit')">Отправить</button>
Родитель решает, как обрабатывать событие:
<BaseButton @submit="handleSubmit" />
Универсальные компоненты позволяют создавать модульные, масштабируемые и легко поддерживаемые интерфейсы, которые можно применять повторно в любом месте приложения, сохраняя чистоту кода и гибкость архитектуры.