Слоты во Vue.js — это механизм композиции компонентов, позволяющий
передавать разметку из родительского компонента в дочерний. В отличие от
передачи данных через props, слоты предназначены именно для
передачи HTML-структуры и логики отображения.
Компонент со слотами выступает как шаблон-контейнер, внутри которого родитель может определить, что именно будет отрисовано. Это делает компоненты гибкими, переиспользуемыми и независимыми от конкретного содержимого.
Простейший слот объявляется с помощью тега
<slot>:
<template>
<div class="card">
<slot></slot>
</div>
</template>
При использовании компонента:
<Card>
<p>Текст внутри карточки</p>
</Card>
Вся переданная разметка подставляется на место
<slot>.
Содержимое слота всегда компилируется в контексте родительского компонента, а не дочернего. Это означает:
<Card>
{{ title }}
</Card>
Переменная title должна существовать в родителе. Это
фундаментальное правило предотвращает неявные зависимости и делает поток
данных предсказуемым.
Слот может иметь запасное содержимое, которое будет отображено, если родитель ничего не передал:
<slot>
<p>Содержимое по умолчанию</p>
</slot>
Такой подход полезен для компонентов, которые должны корректно отображаться даже при минимальной конфигурации.
Когда компоненту требуется несколько независимых областей для вставки контента, используются именованные слоты.
Объявление в дочернем компоненте:
<template>
<div class="modal">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
Использование в родителе:
<Modal>
<template #header>
<h2>Заголовок</h2>
</template>
<p>Основное содержимое</p>
<template #footer>
<button>Закрыть</button>
</template>
</Modal>
Если имя не указано, используется слот по умолчанию.
v-slot и сокращённая формаПолная форма:
<template v-slot:header>
...
</template>
Сокращённая форма:
<template #header>
...
</template>
Для слота по умолчанию:
<template #default>
...
</template>
Или напрямую внутри компонента, если используется только он.
Scoped Slots позволяют дочернему компоненту передавать данные в слот, а родителю — управлять разметкой.
Объявление в дочернем компоненте:
<slot :item="item" :index="index"></slot>
Использование в родителе:
<List>
<template #default="{ item, index }">
<div>{{ index }} — {{ item.name }}</div>
</template>
</List>
Здесь:
Это ключевой механизм для построения абстрактных компонентов списков, таблиц, dropdown-меню.
Scoped Slots поддерживают деструктуризацию:
<template #default="{ user }">
<span>{{ user.email }}</span>
</template>
При необходимости можно переименовывать поля:
<template #default="{ item: product }">
{{ product.price }}
</template>
Это повышает читаемость и избавляет от лишних обращений.
Scoped Slots могут быть именованными:
<slot name="row" :row="rowData"></slot>
Использование:
<template #row="{ row }">
<tr>
<td>{{ row.id }}</td>
<td>{{ row.name }}</td>
</tr>
</template>
Такой подход часто используется в таблицах и сложных UI-компонентах.
Имя слота может быть динамическим:
<slot :name="currentSlot"></slot>
В родителе:
<template #[slotName]>
...
</template>
Используется редко, но полезно в конструкторах интерфейсов и визуальных редакторах.
Проверка наличия слота выполняется через $slots и
$scopedSlots:
<div v-if="$slots.footer">
<slot name="footer"></slot>
</div>
Это позволяет:
Рекомендуется:
props для передачи данныхemit) для уведомленийНе рекомендуется:
propsСлоты — основа построения UI-библиотек:
Пример универсального компонента:
<BaseButton>
<Icon name="save" />
<span>Сохранить</span>
</BaseButton>
Компонент не знает, что внутри — и это его ключевое преимущество.
В отличие от React:
Слоты являются частью шаблонного синтаксиса и интегрированы в реактивную систему Vue.
Слоты позволяют создавать компоненты, которые масштабируются без переписывания кода и сохраняют строгую структуру при полной свободе отображения.