Привет, Хабр!

Когда ты разрабатываешь интерфейсы на Vue.js, рано или поздно приходит момент, когда простые пропсы уже не спасают. Допустим, нужно сделать компонент не просто гибким, а настолько гибким, чтобы его можно было адаптировать под любые сценарии. Пропсами ты тут не отделаешься. И вот тут могут помочь слоты.

Начнём с простого

Что такое слоты? Это такие места в компоненте, куда можно вставлять разметку. Рассмотрим базовый пример:

<template>
  <div class="card">
    <header>
      Default header
    </header>
    <main>
      Default content
    </main>
    <footer>
      Default footer
    </footer>
  </div>
</template>

Здесь есть три слота: один дефолтный и два именованных. Когда используешь этот компонент, можно легко кастомизировать содержимое, вставив свой контент в нужное место:

<template>
  <card>
    <template #default>
      <h1>Мой кастомный заголовок</h1>
    </template>
    <template #main>
      <p>Это мой кастомный контент.</p>
    </template>
    <template #footer>
      <p>Кастомный футер.</p>
    </template>
  </card>
</template>

Результат — компонент кастомизирован именно так, как нужно. У каждого клиента или задачи может быть своя версия контента, и слоты позволяют сделать компонент универсальным.

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

Когда дело доходит до более сложных интерфейсов, нужны уже именованные слоты. Они помогают не просто передавать контент, а разбрасывать его по нужным местам. Например, есть модальное окно с несколькими зонами (заголовок, тело и футер). И вот как можно это реализовать:

<template>
  <div class="modal">
    <div class="modal-header">
      <slot name="header">Default Modal Header</slot>
    </div>
    <div class="modal-body">
      <slot name="body">Default Body Content</slot>
    </div>
    <div class="modal-footer">
      <slot name="footer">Default Modal Footer</slot>
    </div>
  </div>
</template>

Кастомизация может выглядеть так:

<template>
  <modal>
    <template #header>
      <h2>Кастомный заголовок</h2>
    </template>
    <template #body>
      <p>Кастомное тело модального окна.</p>
    </template>
    <template #footer>
      <button>Кнопка для футера</button>
    </template>
  </modal>
</template>

Когда это может пригодиться: В проектах с разными пользователями и ролями. Например, админы видят один набор кнопок, модераторы — другой.

p.s:

Если не передавать контент в слот, будет использоваться контент по умолчанию.

Scoped Slots

Теперь перейдём к Scoped Slots. С ними можно передавать не просто контент, а логику и данные в компонент.

Представим, что есть компонент, который передаёт в слот данные. Например, список элементов:

<template>
  <div>
    <slot :items="items"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: ['Элемент 1', 'Элемент 2', 'Элемент 3']
    };
  }
};
</script>

Теперь можно использовать эти данные для кастомизации компонента:

<template>
  <div>
    <ul>
      <li v-for="item in items" :key="item">{{ item }}</li>
    </ul>
  </div>
</template>

Где может пригодиться: в проектах, когда нужно построить что-то вроде таблицы, где каждая ячейка отображает разные данные в зависимости от логики. Например, можно рендерить разные элементы в зависимости от роли пользователя или состояния приложения.

Динамические компоненты

Допустим, нужно что-то ещё более сложное — компонент, который сам решает, какие слоты ему нужны, и когда. Например, в зависимости от состояния или данных. Здесь помогают динамические компоненты.

Пример:

<template>
  <component :is="dynamicComponent">
    <template #header>Заголовок</template>
    <template #body>Основной контент</template>
  </component>
</template>

<script>
export default {
  data() {
    return {
      dynamicComponent: 'CardComponent'  // Это может быть любое имя компонента
    };
  }
};
</script>

Можно менять компонент динамически, в зависимости от логики приложения.

В одном проекте у нас был интерфейс с виджетами, которые должны были меняться на лету в зависимости от данных. Динамические компоненты позволяют создавать интерфейсы, которые автоматически подстраиваются под любые данные, без необходимости дублировать код.


Что дальше?

Если слоты уже не кажутся cложностью, стоит углубиться в такие темы, как:

  • Композиционное API — для более гибкого построения логики компонентов.

  • Рендер-функции — когда нужно ещё больше контроля над тем, как рендерятся компоненты.

23 октября для новичков в Vue.js пройдет открытый урок «Composition API против Options API: что выбрать?». Полученные на уроке знания будут полезны при любом взаимодействии с Vue.js, так как это основы работы. Если интересно — записывайтесь на урок на странице курса «Vue.js разработчик».

Комментарии (3)


  1. Metotron0
    17.10.2024 07:38

    У вас в разделе "Именованные слоты" поменялось только то, что вы заменили header на div с классом. А слоты как были именованными уже в предыдущем примере, так и остались.

    А ещё у вас, как я понял, поясняющий текст идёт до картинки, из-за этого кажется, что "Здесь есть три слота:" относится к верхней картинке. Или же вы назвали слотами места, где нужны будут слоты, но их пока нет.


  1. nin-jin
    17.10.2024 07:38

    А вот в $mol каждый вложенный компонент и плейсхолдер уже является "слотом" без доп кода. При этом из одного слота даже снаружи можно использовать другие внутренние слоты. Завидуйте.


    1. FireLynx
      17.10.2024 07:38

      А можно не будем?