Слоты и их применение

Слоты во 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)

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

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-библиотек:

  • кнопки с кастомным контентом
  • карточки с произвольной структурой
  • списки и таблицы с настраиваемыми строками
  • layout-компоненты (header / sidebar / content)

Пример универсального компонента:

<BaseButton>
  <Icon name="save" />
  <span>Сохранить</span>
</BaseButton>

Компонент не знает, что внутри — и это его ключевое преимущество.


Отличие слотов от children в других фреймворках

В отличие от React:

  • Vue поддерживает несколько слотов
  • есть встроенный механизм передачи данных в слот
  • шаблон остаётся декларативным и читаемым

Слоты являются частью шаблонного синтаксиса и интегрированы в реактивную систему Vue.


Практические сценарии применения

  • UI-киты и дизайн-системы
  • обёртки для сторонних библиотек
  • динамические формы
  • таблицы с кастомными ячейками
  • модальные окна и всплывающие панели

Слоты позволяют создавать компоненты, которые масштабируются без переписывания кода и сохраняют строгую структуру при полной свободе отображения.