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 наружу, давая родителю решать его вёрстку