Содержимое слота
Мы узнали, что компоненты могут принимать реквизиты, которые могут быть значениями JavaScript любого типа. А как насчет содержимого шаблона? В некоторых случаях мы можем захотеть передать фрагмент шаблона дочернему компоненту и позволить дочернему компоненту отображать этот фрагмент в своем собственном шаблоне.
Например, у нас может быть FancyButton компонент, поддерживающий такое использование
Шаблон FancyButton выглядит следующим образом
Этот slot элемент представляет собой выход слота, который указывает, где должно отображаться содержимое слота, предоставленное родительским элементом
И финальный рендеринг DOM
В случае со слотами элемент FancyButtonотвечает за внешний вид button(и его причудливый стиль), тогда как внутреннее содержимое предоставляется родительским компонентом
Другой способ понять слоты — сравнить их с функциями JavaScript
Содержимое слота не ограничивается только текстом. Это может быть любое допустимое содержимое шаблона. Например, мы можем передать несколько элементов или даже другие компоненты
Использование слотов делает нашу FancyButtonсистему более гибкой и многоразовой. Теперь мы можем использовать его в разных местах с разным внутренним содержимым, но с одинаковым причудливым стилем.
Механизм слотов компонентов Vue вдохновлен собственным элементом Web Component slot , но с дополнительными возможностями, которые мы увидим позже.
Область рендеринга
Содержимое слота имеет доступ к области данных родительского компонента, поскольку оно определено в родительском компоненте.
Здесь обе {{ message }} интерполяции будут отображать один и тот же контент.
Содержимое слота не имеет доступа к данным дочернего компонента. Выражения в шаблонах Vue имеют доступ только к той области, в которой они определены, в соответствии с лексической областью действия JavaScript. Другими словами
Выражения в родительском шаблоне имеют доступ только к родительской области; выражения в дочернем шаблоне имеют доступ только к дочерней области
Резервный контент
В некоторых случаях полезно указать запасной контент (то есть контент по умолчанию) для слота, который будет отображаться только тогда, когда контент не предоставлен. Например, в SubmitButton компоненте
Мы могли бы захотеть, чтобы текст «Отправить» отображался внутри, buttonесли родительский элемент не предоставил никакого содержимого слота. Чтобы сделать «Отправить» резервным контентом, мы можем поместить его между slotтегами
Теперь, когда мы используем SubmitButtonродительский компонент, не предоставляя содержимого для слота:
Это отобразит резервный контент «Отправить»:
Но если мы предоставим контент:
Тогда вместо этого будет отображаться предоставленный контент:
Именованные слоты
Бывают случаи, когда полезно иметь несколько гнездовых розеток в одном компоненте. Например, в BaseLayout компоненте со следующим шаблоном
В таких случаях у slot элемента есть специальный атрибут , nameкоторый можно использовать для присвоения уникальных идентификаторов различным слотам, чтобы вы могли определить, где следует отображать контент:
Розетка slotбез nameнеявно имеет имя «по умолчанию».
В родительском компоненте, использующем BaseLayout, нам нужен способ передачи нескольких фрагментов содержимого слота, каждый из которых нацелен на отдельный выход слота. Здесь на помощь приходят именованные слоты
Чтобы передать именованный слот, нам нужно использовать template элемент с v-slot директивой, а затем передать имя слота в качестве аргумента v-slot
v-slot имеет специальную стенографию #, поэтому template v-slot:header его можно сократить до просто template #header. Думайте об этом как о «отрисовке этого фрагмента шаблона в слоте заголовка дочернего компонента»

Вот код, передающий содержимое для всех трех слотов с BaseLayoutиспользованием сокращенного синтаксиса
Когда компонент принимает как слот по умолчанию, так и именованные слоты, все templateузлы верхнего уровня, не являющиеся узлами, неявно рассматриваются как содержимое слота по умолчанию. Таким образом, вышеизложенное также можно записать как
Теперь все, что находится внутри template элементов, будет передано в соответствующие слоты. Окончательный визуализированный HTML будет:
Опять же, это может помочь вам лучше понять именованные слоты, используя аналогию с функцией JavaScript
Динамические имена слотов
Аргументы динамических директив также работают с v-slot, позволяя определять имена динамических слотов
Обратите внимание, что на выражение распространяются синтаксические ограничения аргументов динамической директивы
Слоты с ограниченной областью действия
Как обсуждалось в разделе Render Scope , содержимое слота не имеет доступа к состоянию дочернего компонента.
Однако в некоторых случаях может быть полезно, если содержимое слота может использовать данные как из родительской, так и из дочерней области. Чтобы добиться этого, нам нужен способ, с помощью которого дочерний элемент может передавать данные в слот при его рендеринге.
Фактически, мы можем сделать именно это — мы можем передавать атрибуты в слот-выход точно так же, как передаем реквизиты компоненту:
Получение реквизитов слота немного отличается при использовании одного слота по умолчанию и при использовании именованных слотов. Сначала мы собираемся показать, как получать реквизиты, используя один слот по умолчанию, используя v-slot непосредственно тег дочернего компонента:
Свойства, переданные в слот дочерним элементом, доступны как значение соответствующей v-slot директивы, доступ к которой можно получить с помощью выражений внутри слота.
Вы можете думать о слоте с ограниченной областью действия как о функции, передаваемой в дочерний компонент. Затем дочерний компонент вызывает его, передавая реквизиты в качестве аргументов:
Фактически, это очень похоже на то, как компилируются слоты с ограниченной областью действия и как вы могли бы использовать слоты с ограниченной областью действия в функциях ручного рендеринга .
Обратите внимание, как v-slot="slotProps" совпадает сигнатура функции слота. Как и в случае с аргументами функции, мы можем использовать деструктуризацию в v-slot
Именованные слоты с областью действия
Именованные слоты с областью действия работают аналогично — свойства слота доступны по значению директивы v-slot: v-slot:name="slotProps" При использовании сокращения это выглядит так
Передача реквизита в именованный слот
Обратите внимание, что nameслот не будет включен в реквизиты, поскольку он зарезервирован, поэтому результатом headerProps будет { message: 'hello' }
Если вы смешиваете именованные слоты со слотом по умолчанию, вам необходимо использовать явный template тег для слота по умолчанию. Попытка разместить v-slot директиву непосредственно в компоненте приведет к ошибке компиляции. Это сделано для того, чтобы избежать какой-либо двусмысленности в отношении объема реквизитов слота по умолчанию
Использование явного templateтега для слота по умолчанию помогает понять, что реквизит message недоступен внутри другого слота
Пример необычного списка
Вам может быть интересно, как лучше всего использовать слоты с ограниченной областью действия. Вот пример: представьте себе FancyListкомпонент, который отображает список элементов — он может инкапсулировать логику загрузки удаленных данных, использования данных для отображения списка или даже расширенных функций, таких как нумерация страниц или бесконечная прокрутка. Однако мы хотим, чтобы внешний вид каждого элемента был гибким, и оставляли стиль каждого элемента на усмотрение родительского компонента, который его использует. Таким образом, желаемое использование может выглядеть так
Внутри FancyList мы можем визуализировать одно и то же slot несколько раз с разными данными элемента (обратите внимание, что мы используем v-bind для передачи объекта в качестве реквизита слота)
Компоненты без рендеринга
Вариант FancyListиспользования, который мы обсуждали выше, инкапсулирует как повторно используемую логику (извлечение данных, разбиение на страницы и т. д.), так и визуальный вывод, при этом делегируя часть визуального вывода потребительскому компоненту через слоты с ограниченной областью действия.
Если мы продвинем эту концепцию немного дальше, мы сможем создать компоненты, которые только инкапсулируют логику и ничего не визуализируют сами по себе — визуальный вывод полностью делегируется потребительскому компоненту с ограниченными слотами. Мы называем этот тип компонента компонентом без рендеринга .
Примером компонента без рендеринга может быть тот, который инкапсулирует логику отслеживания текущего положения мыши:
Хотя это интересный шаблон, большая часть того, чего можно достичь с помощью компонентов без рендеринга, может быть достигнута более эффективным способом с помощью Composition API, без дополнительных затрат на вложение компонентов. Посмотрите реализовать ту же функциональность отслеживания мыши в Composable
Тем не менее, слоты с ограниченной областью по-прежнему полезны в тех случаях, когда нам нужно как инкапсулировать логику , так и составить визуальный вывод, как в FancyList примере