Что такое слоты и зачем они нужны?
В Vue слоты (slots) — это механизм для вставки пользовательского контента в шаблон компонента. Они позволяют создавать более гибкие и переиспользуемые компоненты, в которые можно передавать разную разметку или структуру из родительского компонента.
Слоты работают как замещаемые места в шаблоне, куда родительский компонент может вставить произвольный HTML или Vue-компоненты.
Простые (одиночные) слоты
Обычный слот создаётся с помощью <slot></slot>. Всё, что передаётся внутрь компонента между его тегами, будет вставлено в это место.
Пример: компонент BaseCard.vue
<template>
<div class="card">
<slot></slot>
</div>
</template>
Использование:
<BaseCard>
<p>Привет, это содержимое карточки</p>
</BaseCard>
Результат: <p> вставится внутрь div.card.
Слот по умолчанию и запасное содержимое
Если родитель ничего не передал, можно отобразить содержимое по умолчанию:
<template>
<div>
<slot>Текст по умолчанию</slot>
</div>
</template>
Именованные слоты
Когда компонент должен принимать несколько отдельных участков содержимого, используются именованные слоты.
Пример: компонент BaseLayout.vue
<template>
<div class="layout">
<header><slot name="header" /></header>
<main><slot /></main>
<footer><slot name="footer" /></footer>
</div>
</template>
Использование:
<BaseLayout>
<template v-slot:header>
<h1>Заголовок</h1>
</template>
<p>Основной контент страницы</p>
<template v-slot:footer>
<p>Футер © 2025</p>
</template>
</BaseLayout>
Если контент без v-slot — он идёт в дефолтный слот (<slot />), остальные — по имени.
Сокращённая запись v-slot
Можно использовать сокращение #имя:
<template #header>
<h1>Сокращённая запись</h1>
</template>
Для дефолтного слота: v-slot:default или #default.
Скопированные атрибуты в слотах (атрибут v-bind)
Vue позволяет передавать данные из дочернего компонента в слот с помощью scope slots (слоты с областью видимости). Это полезно, если компонент должен управлять частью данных, но внешний шаблон — управлять отображением.
Пример: компонент ItemList.vue
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item" />
</li>
</ul>
</template>
<script>
export default {
props: \['items'\]
}
</script>
Использование с родительской стороны:
<ItemList :items="products">
<template v-slot:default="{ item }">
<strong>{{ item.name }}</strong>: {{ item.price }}$
</template>
</ItemList>
В этом примере дочерний компонент управляет списком, но родитель управляет тем, как каждый элемент отрисовывается.
Пример для компонентов таблиц
Компонент таблицы может иметь слот для кастомного отображения ячеек:
<Table :rows="users">
<template v-slot:cell-name="{ row }">
<a :href="\`/user/${row.id}\`">{{ row.name }}</a>
</template>
</Table>
А компонент Table.vue внутри будет вызывать слот:
<td>
<slot name="cell-name" :row="user">{{ user.name }}</slot>
</td>
Совместное использование слотов и пропсов
Компоненты могут передавать данные через слот, чтобы родитель получил возможность на их основе построить шаблон. Это особенно важно, если данные внутри компонента, а внешний код должен их визуализировать.
Вложенные компоненты со слотами
Слоты поддерживаются и во вложенных структурах, но важно следить за тем, как они пробрасываются.
<BaseLayout>
<template #header>
<MyNav />
</template>
<template #default>
<MainContent />
</template>
</BaseLayout>
BaseLayout может использовать слоты как контейнер, при этом весь контент определяется снаружи.
Динамические имена слотов
В обычной практике Vue не поддерживает динамически вычисляемые имена слотов, но с помощью v-if и v-slot можно управлять тем, какие слоты активны, на основе данных:
<component :is="layout">
<template v-slot:\[activeSlot\]>
<p>Контент активного слота</p>
</template>
</component>
Комбинация с <component :is="...">
Вместе со слотами можно динамически управлять не только содержимым, но и тем, в какой компонент вставляется контент:
<component :is="currentView">
<template #default>
<p>Универсальный контент</p>
</template>
</component>
Примеры из практики
Модальные окна:
<Modal>
<template #header>
<h2>Подтвердите действие</h2>
</template>
<p>Вы уверены, что хотите удалить?</p>
<template #footer>
<button>Отмена</button>
<button>Удалить</button>
</template>
</Modal>
Модальное окно становится полностью переопределяемым, но логика его открытия/закрытия — остаётся в компоненте.
Кастомизация выпадающих списков
<Dropdown :options="items">
<template #option="{ option }">
<div class="dropdown-option">
<img :src="option.icon" />
<span>{{ option.label }}</span>
</div>
</template>
</Dropdown>
Позволяет полностью кастомизировать отображение каждого пункта выпадающего списка.
Когда стоит использовать слоты
Слоты особенно полезны, если:
-
Компонент не знает заранее, как будет выглядеть контент;
-
Нужно дать пользователю гибкость в кастомизации;
-
Повторяются одинаковые шаблоны с разным содержанием;
-
Требуется переиспользуемая структура (карточки, модалки, таблицы, лэйауты).
Слоты — это мощный инструмент Vue для создания гибких, универсальных и масштабируемых компонентов, в которых разделяется ответственность между логикой и отображением.