Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Слоты

Props передают в компонент данные, а слоты — разметку. Компонент оставляет в своём шаблоне место, тег slot, а родитель решает, что туда вставить. Так делают обёртки: карточку, модальное окно, список с заголовком.

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

Создайте src/components/BaseCard.vue:

<!-- src/components/BaseCard.vue -->
<template>
  <section class="card">
    <slot />
  </section>
</template>
 
<style scoped>
.card {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 16px;
}
</style>

Всё, что родитель напишет между открывающим и закрывающим тегом компонента, окажется на месте slot:

<!-- src/App.vue -->
<script setup>
import BaseCard from './components/BaseCard.vue'
</script>
 
<template>
  <BaseCard>
    <h2>Сегодня</h2>
    <p>Купить хлеб, позвонить Анне.</p>
  </BaseCard>
</template>

На странице появится рамка со скруглёнными углами, а внутри — заголовок и абзац из App.vue.

Содержимое по умолчанию

Текст внутри тега slot показывается, если родитель ничего не передал:

<template>
  <section class="card">
    <slot>Пусто</slot>
  </section>
</template>

<BaseCard /> без содержимого выведет рамку со словом «Пусто».

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

Если мест для вставки несколько, им дают имена. Создайте src/components/TodoList.vue:

<!-- src/components/TodoList.vue -->
<template>
  <div class="todo-list">
    <header><slot name="header" /></header>
    <ul><slot /></ul>
    <footer><slot name="footer" /></footer>
  </div>
</template>

Родитель указывает слот через template с директивой v-slot или её короткой формой #:

<!-- src/App.vue, фрагмент шаблона -->
<TodoList>
  <template #header>
    <h2>Мои задачи</h2>
  </template>
 
  <TodoItem v-for="todo in todos" :key="todo.id" :todo="todo" />
 
  <template #footer>
    Осталось: {{ remaining }}
  </template>
</TodoList>

Заголовок попадёт в header, задачи — в слот по умолчанию внутри ul, счётчик — в footer.

Слоты с данными

Иногда компонент перебирает данные сам, а вид строки задаёт родитель. Компонент передаёт данные в слот атрибутами:

<!-- src/components/TodoList.vue -->
<script setup>
defineProps({ todos: Array })
</script>
 
<template>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      <slot :todo="todo" />
    </li>
  </ul>
</template>
<!-- src/App.vue, фрагмент шаблона -->
<TodoList :todos="todos" v-slot="{ todo }">
  <strong v-if="todo.done">{{ todo.text }}</strong>
  <span v-else>{{ todo.text }}</span>
</TodoList>

Выполненные задачи выводятся жирным, остальные — обычным текстом. Родитель получил todo из компонента через v-slot.

Частая ошибка

Ждать, что содержимое слота видит переменные дочернего компонента. Разметка в слоте пишется в шаблоне родителя и имеет доступ только к данным родителя. Если внутри слота нужна переменная компонента, компонент должен явно передать её атрибутом, как :todo="todo" выше.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.