Как использовать 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 слоты отлично работают с динамическими компонентами:
<component :is="MyComponent" v-slot="{ someData }">
{{ someData }}
</component>
Использование с render-функциями (JSX/TSX)
В рендер-функции можно передавать scoped slots как функции:
<MyComponent v-slots={{
default: ({ item }) => <div>{ item.name }</div>
}} />
Передача нескольких параметров
Дочерний может передавать сколько угодно параметров:
<slot :user="user" :index="index" />
В родителе:
<UserList v-slot="{ user, index }">
<p>{{ index + 1 }}. {{ user.name }}</p>
</UserList>
Значения по умолчанию в scoped slot
Если родитель не передал слот, можно отрисовать fallback:
<slot :user="user">
{{ user.name }}
</slot>
Scoped slot в v-for
Можно использовать scoped slot прямо в v-for в родителе:
<UserList>
<template #default="{ user }">
<div>{{ user.name }}</div>
</template>
</UserList>
Пример: компонент “Select”
Компонент MySelect.vue:
<template>
<select>
<slot v-for="option in options" :option="option" :key="option.value" />
</select>
</template>
<script setup>
defineProps({
options: Array
});
</script>
Использование:
<MySelect :options="cities">
<template #default="{ option }">
<option :value="option.value">{{ option.label }}</option>
</template>
</MySelect>
<script setup>
const cities = \[
{ value: 'tashkent', label: 'Ташкент' },
{ value: 'samarkand', label: 'Самарканд' }
\];
</script>
Scoped slots в Vue — это способ делегировать контроль над рендерингом родителю, но при этом оставить бизнес-логику и структуру внутри компонента. Это особенно полезно для создания переиспользуемых UI-компонентов: таблиц, списков, выпадающих меню, графиков, форм, и других интерфейсных блоков.