Vue

Слоты: обычные, именованные, scoped

Компонент Card в дизайн-системе задаёт рамку, тень и отступы, но содержимое у каждой карточки своё: где-то текст, где-то форма, где-то график. Если передавать содержимое строкой через проп, теряется вся гибкость разметки. Слот переворачивает направление: не данные текут в компонент, а кусок шаблона. Card рисует обёртку, а что внутри - решает родитель. Компоненты вроде модалки, таблицы и списка в UI-библиотеках построены именно на слотах, потому что иначе под каждый вариант содержимого пришлось бы плодить пропы.

  • Card и Panel в дизайн-системе: рамка и отступы от компонента, произвольное содержимое через слот по умолчанию
  • Modal с именованными слотами header, body и footer: каждая зона настраивается отдельно
  • Таблица, отдающая ячейку строки через scoped-слот, чтобы родитель сам сверстал её содержимое
  • Список с scoped-слотом на элемент: логика перебора в компоненте, вёрстка строки у родителя

Предварительные знания

  • Однофайловые компоненты и их вложенность
  • Шаблонный синтаксис и интерполяция
  • Понимание потока props от родителя к ребёнку

Слот по умолчанию и fallback

Слот - это объявленная компонентом точка, куда родитель вставит свою разметку. Компонент пишет тег slot там, где должно появиться переданное содержимое. Родитель кладёт разметку между открывающим и закрывающим тегами компонента, и она подставляется в слот. Если родитель ничего не передал, показывается fallback - содержимое, написанное внутри самого тега slot.

Ключевое отличие от props: через слот передаётся разметка (элементы, компоненты, текст), а не значение. Компонент управляет обёрткой и стилем, а наполнение задаёт родитель. Это инверсия по сравнению с props, где данные текут внутрь.

Что показывает компонент, если внутри тега slot написано содержимое, а родитель ничего не передал?

Именованные слоты

Когда у компонента несколько зон вставки (шапка, тело, подвал), их различают именами. Компонент объявляет <slot name="header">, а родитель направляет содержимое в нужную зону через директиву v-slot:header на теге template. Сокращённая запись v-slot - символ #, поэтому пишут #header. Слот без имени остаётся слотом по умолчанию.

Содержимое без обёртки template и без указания слота автоматически попадает в слот по умолчанию. В примере выше абзац с вопросом уходит в безымянный <slot />, поэтому его не оборачивают в template.

Директива #header - это короткая форма v-slot:header. Обе записи эквивалентны, как @ для v-on и : для v-bind. В современном коде почти всегда пишут короткую форму с решёткой.

Как родитель направляет содержимое в именованный слот <slot name="footer" />?

Scoped-слоты: данные обратно родителю

Обычный слот передаёт разметку из родителя в ребёнка. Scoped-слот добавляет обратный канал: компонент привязывает к тегу slot свои данные, а родитель получает их в шаблоне слота. Так логика (перебор, загрузка, фильтрация) живёт в компоненте, а вёрстку каждого элемента задаёт родитель, используя предоставленные данные.

Атрибуты на теге slot (:user, :index) образуют объект slot props. Родитель деструктурирует его в v-slot="{ user }". Получается разделение ответственности: компонент знает, как перебрать и что отдать, а родитель - как это показать.

Чем scoped-слот отличается от обычного именованного слота?

Когда слот, а когда проп

Слоты и props решают разные задачи. Проп передаёт данные, которые компонент сам превратит в разметку. Слот передаёт готовую разметку, оставляя контроль над ней родителю. Если родителю нужно задать произвольное содержимое или его структуру - это слот. Если достаточно передать значение, а вид определяет компонент - это проп.

  • Проп — Передаёт данные. Вид содержимого определяет сам компонент. Пример: :title="'Заголовок'"
  • Слот — Передаёт разметку. Вид содержимого определяет родитель. Пример: произвольный HTML внутри Card
ЗадачаИнструмент
Передать строку заголовкаПроп
Вставить произвольную разметку в карточкуСлот по умолчанию
Настроить шапку, тело и подвал по отдельностиИменованные слоты
Задать вёрстку строки списка, оставив перебор компонентуScoped-слот

Признак того, что нужен слот, а не проп: значение пропа начинает разрастаться в HTML-строку или в набор флагов вида showIcon, iconPosition, iconColor. Это сигнал отдать разметку родителю через слот вместо описания всех вариантов пропами.

Компонент кнопки оброс пропами showIcon, iconName, iconPosition, iconColor для настройки иконки. О чём это говорит?

Связь с другими темами

Слоты дополняют props как канал передачи разметки между компонентами:

  • Связь компонентов — Слот - альтернатива props: родитель передаёт ребёнку не значение, а фрагмент шаблона
  • Условия и списки — Scoped-слот отдаёт наружу элемент v-for, оставляя вёрстку строки родителю

Итог

  • Слот - точка вставки разметки: родитель кладёт содержимое, а компонент рисует его в месте slot
  • Слот по умолчанию - один безымянный; содержимое между тегами компонента попадает в него
  • Именованные слоты разделяют зоны: <slot name="header"> заполняется через v-slot:header или #header
  • Fallback-содержимое внутри тега slot показывается, когда родитель ничего не передал
  • Scoped-слот отдаёт данные обратно родителю: компонент привязывает их к slot, родитель принимает через v-slot="{ ... }"

Связанные уроки

  • vue-10-component-communication — Слоты - ещё один канал связи родителя и ребёнка: передают разметку, тогда как props передают данные
  • vue-13-conditional-list — Scoped-слот часто отдаёт элемент списка из v-for наружу, давая родителю решать его вёрстку
Слоты: обычные, именованные, scoped

0

1

Войти