Ну что ж, если вы добрались до этой статьи, значит, вы уже довольно уверенно чувствуете себя в мире Vue и готовы к тому, чтобы подняться на новый уровень — разобраться с продвинутыми компонентами. Этот раздел откроет вам доступ к таким мощным инструментам Vue.js, как динамические компоненты, функциональные компоненты, и кастомные директивы. Настало время сделать ваше приложение по-настоящему гибким и модульным.
Динамические компоненты
Динамическими компонентами называют такие компоненты, которые могут изменяться на лету в зависимости от условий. Это особенно полезно, когда у вас есть несколько похожих компонентов, и вам нужно динамически переключаться между ними. Например, в интернет-магазине у вас может быть один компонент для отображения товара в списке, и другой — для подробной карточки товара.
Для этого Vue.js предлагает специальный компонент — <component>. Он позволяет переключаться между компонентами в зависимости от переданного значения.
Давайте рассмотрим пример. Представим, что у нас есть два компонента — ProductList и ProductDetails, и нам нужно переключаться между ними в зависимости от того, что хочет видеть пользователь.
<template>
<div>
<button @click="view = 'list'">Показать список</button>
<button @click="view = 'details'">Показать детали</button>
<component :is="view"></component>
</div>
</template>
<script setup>
import ProductList from './components/ProductList.vue';
import ProductDetails from './components/ProductDetails.vue';
import { ref } from 'vue';
const view = ref('list');
</script>
Здесь мы используем директиву :is, чтобы динамически переключаться между компонентами. Переменная view может принимать значения ‘list’ и ‘details’, что указывает Vue на то, какой компонент рендерить.
Почему это круто? Потому что это позволяет вам не загромождать код кучей условий (v-if и v-else), а динамически подгружать нужный компонент по мере необходимости, что сильно упрощает структуру кода и повышает его читабельность.
Асинхронные компоненты
Представьте, что ваше приложение растёт, и количество компонентов увеличивается. Загружать их все сразу — не лучшая идея, особенно если некоторые из них нужны только при определённых условиях. Vue.js решает эту проблему с помощью асинхронных компонентов.
Асинхронный компонент загружается только тогда, когда он действительно нужен. Например, у вас есть тяжёлый компонент с графиками и аналитикой, который нужен только на одной странице. Загружать его вместе со всем приложением — неэффективно.
Вот пример, как это делается:
<template>
<div>
<component :is="AsyncComponent"></component>
</div>
</template>
<script setup>
import { defineAsyncComponent } from 'vue';
const AsyncComponent = defineAsyncComponent(() =>
import('./components/HeavyComponent.vue')
);
</script>
Теперь компонент HeavyComponent будет загружаться только при необходимости. Это экономит ресурсы и ускоряет начальную загрузку вашего приложения.
Функциональные компоненты
Функциональные компоненты — это легковесные компоненты, которые не имеют собственного состояния и жизненных циклов. Они исключительно принимают данные через пропсы и возвращают шаблон. Это как чистые функции в программировании — идеальны для компонентов, которые просто рендерят данные и не зависят от контекста или состояния.
Создать функциональный компонент в Vue.js проще простого. Вам нужно использовать опцию functional в объявлении компонента:
<template functional>
<div>
<p>{{ props.text }}</p>
</div>
</template>
<script setup>
const props = defineProps({
text: String
});
</script>
Функциональные компоненты хорошо подходят для таких вещей, как элементы списка, заголовки, маленькие переиспользуемые элементы интерфейса.
Почему стоит их использовать? Потому что они не имеют состояния и не создают дополнительных реактивных объектов, что делает их быстрее обычных компонентов.
Обработка слотов — scoped slots
Мы уже говорили о слотах в предыдущих статьях, но давайте копнём глубже и разберём scoped slots — это возможность передавать данные из дочернего компонента в родительский через слоты.
Представим ситуацию: у вас есть компонент карточки товара, и вы хотите, чтобы родительский компонент мог настраивать отображение отдельных элементов этой карточки (например, картинку, цену или описание), передавая не просто HTML, но и данные.
Вот простой пример:
<!-- Card.vue -->
<template>
<div class="card">
<slot :product="product"></slot>
</div>
</template>
<script setup>
const props = defineProps({
product: Object
});
</script>
В родительском компоненте мы можем использовать этот слот и получать данные product для кастомного рендеринга:
<template>
<Card :product="product">
<template v-slot="{ product }">
<h2>{{ product.name }}</h2>
<p>{{ product.price }}</p>
</template>
</Card>
</template>
<script setup>
const product = {
name: 'Телефон',
price: 500
};
</script>
Это позволяет вам передавать данные и кастомизировать содержимое слотов на лету.
Provide и Inject
Когда приложение растёт, иногда нужно передавать данные между компонентами, которые не находятся в непосредственной иерархии (например, не являются родительским и дочерним компонентом). Для этого в Vue.js есть механизмы provide/inject.
Этот подход позволяет «поставлять» данные на верхнем уровне и «инжектировать» их в любом компоненте глубже по дереву компонентов.
Пример:
<!-- App.vue -->
<template>
<ParentComponent />
</template>
<script setup>
import { provide } from 'vue';
const theme = ref('dark');
provide('theme', theme);
</script>
В любом глубоком компоненте мы можем инжектировать этот параметр:
<script setup>
import { inject } from 'vue';
const theme = inject('theme');
</script>
<template>
<div :class="theme">
Текущая тема: {{ theme }}
</div>
</template>
Такое решение отлично подходит для передачи глобальных настроек, например, темы оформления или языка, везде, где это нужно, без необходимости передавать данные через пропсы.
Кастомные директивы
Vue.js поддерживает не только стандартные директивы (v-if, v-for, v-model), но и позволяет создавать свои собственные кастомные директивы. Например, вы можете создать директиву, которая автоматически фокусирует поле ввода, как только оно появляется на экране:
<template>
<input v-focus />
</template>
<script setup>
import { onMounted } from 'vue';
const focus = {
mounted(el) {
el.focus();
}
};
defineDirective('focus', focus);
</script>
С помощью кастомных директив можно решить множество задач, таких как создание пользовательских анимаций, управление фокусом или даже создание собственных событий.