Как использовать scoped slots?

Scoped slots (слоты с областью видимости) в Vue позволяют дочернему компоненту передавать данные родительскому через слот. Это мощный способ делать компоненты гибкими и переиспользуемыми, предоставляя родителю контроль над тем, как отобразить контент с использованием данных из дочернего компонента.

Стандартные слоты vs Scoped slots

  • Обычные слоты передают разметку от родителя к дочернему компоненту.

  • Scoped слоты позволяют дочернему компоненту передавать данные родителю, чтобы родитель сам решил, как их отрисовать.

Синтаксис scoped slot'ов (Vue 3)

Дочерний компонент (UserList.vue):

<template>
<ul>
<li v-for="user in users" :key="user.id">
<slot :user="user">{{ user.name }}</slot>
</li>
</ul>
</template>
<script setup>
const users = \[
{ id: 1, name: 'Алиса' },
{ id: 2, name: 'Боб' }
\];
</script>
  • Слот получает объект user для каждого элемента массива.

  • Значение по умолчанию: {{ user.name }} — будет показано, если родитель не передал свой слот.

Родительский компонент:

<template>
<UserList>
<template #default="{ user }">
<strong>{{ user.name }}</strong> (ID: {{ user.id }})
</template>
</UserList>
</template>
<script setup>
import UserList from './UserList.vue';
</script>
  • default="{ user }" — это деструктуризация объекта, переданного из дочернего компонента.

  • Родитель сам определяет, как отображать данные пользователя.

Сложный пример с именованными scoped слотами

Scoped slots можно использовать не только для default, но и для именованных слотов.

Компонент Table.vue:

<template>
<table>
<thead>
<tr>
<slot name="header" />
</tr>
</thead>
<tbody>
<tr v-for="row in rows" :key="row.id">
<slot name="row" :row="row" />
</tr>
</tbody>
</table>
</template>
<script setup>
defineProps({
rows: Array
});
</script>

Родитель:

<template>
<Table :rows="people">
<template #header>
<th>Имя</th>
<th>Возраст</th>
</template>
<template #row="{ row }">
<td>{{ row.name }}</td>
<td>{{ row.age }}</td>
</template>
</Table>
</template>
<script setup>
import Table from './Table.vue';
const people = \[
{ id: 1, name: 'Алиса', age: 30 },
{ id: 2, name: 'Боб', age: 25 }
\];
</script>
  • row="{ row }" — доступ к каждой строке.

  • Таким способом родитель полностью управляет отображением содержимого таблицы, хотя логика цикла и структура таблицы остаются в дочернем компоненте.

Альтернативный синтаксис с v-slot

Можно использовать v-slot вместо #:

<UserList v-slot="{ user }">
<p>{{ user.name }}</p>
</UserList>

Для именованных слотов:

<Table>
<template v-slot:header>
<th>Имя</th>
</template>
<template v-slot:row="{ row }">
<td>{{ row.name }}</td>
</template>
</Table>

Сокращение работает только с default слотом (#default, v-slot), для остальных нужно явно указывать имя.

Использование scoped слота внутри <component :is="...">

Scoped слоты отлично работают с динамическими компонентами:

&lt;component :is="MyComponent" v-slot="{ someData }"&gt;
{{ someData }}
&lt;/component&gt;

Использование с render-функциями (JSX/TSX)

В рендер-функции можно передавать scoped slots как функции:

<MyComponent v-slots={{
default: ({ item }) => &lt;div&gt;{ item.name }&lt;/div&gt;
}} />

Передача нескольких параметров

Дочерний может передавать сколько угодно параметров:

&lt;slot :user="user" :index="index" /&gt;

В родителе:

&lt;UserList v-slot="{ user, index }"&gt;
&lt;p&gt;{{ index + 1 }}. {{ user.name }}&lt;/p&gt;
&lt;/UserList&gt;

Значения по умолчанию в scoped slot

Если родитель не передал слот, можно отрисовать fallback:

&lt;slot :user="user"&gt;
{{ user.name }}
&lt;/slot&gt;

Scoped slot в v-for

Можно использовать scoped slot прямо в v-for в родителе:

&lt;UserList&gt;
&lt;template #default="{ user }"&gt;
&lt;div&gt;{{ user.name }}&lt;/div&gt;
&lt;/template&gt;
&lt;/UserList&gt;

Пример: компонент “Select”

Компонент MySelect.vue:

&lt;template&gt;
&lt;select&gt;
&lt;slot v-for="option in options" :option="option" :key="option.value" /&gt;
&lt;/select&gt;
&lt;/template&gt;
&lt;script setup&gt;
defineProps({
options: Array
});
&lt;/script&gt;

Использование:

&lt;MySelect :options="cities"&gt;
&lt;template #default="{ option }"&gt;
&lt;option :value="option.value"&gt;{{ option.label }}&lt;/option&gt;
&lt;/template&gt;
&lt;/MySelect&gt;
&lt;script setup&gt;
const cities = \[
{ value: 'tashkent', label: 'Ташкент' },
{ value: 'samarkand', label: 'Самарканд' }
\];
&lt;/script&gt;

Scoped slots в Vue — это способ делегировать контроль над рендерингом родителю, но при этом оставить бизнес-логику и структуру внутри компонента. Это особенно полезно для создания переиспользуемых UI-компонентов: таблиц, списков, выпадающих меню, графиков, форм, и других интерфейсных блоков.