Композиция компонентов через слоты

Слоты в Stencil играют важную роль при создании многоразовых и гибких компонентов, позволяя разработчикам вставлять элементы внутри компонента, сохраняя его независимость и переиспользуемость. Механизм слотов в Stencil напоминает его аналог в других фреймворках, таких как Web Components, и предоставляет способ вставки контента в заранее определённые места внутри компонента. Слоты позволяют создавать более универсальные компоненты, которые могут быть настроены без изменения их исходного кода.

Основы работы с слотами

В Stencil слоты используют стандарт HTML-элемент <slot>. Эти элементы выполняют роль контейнеров, в которые можно вставлять контент, определённый извне, при этом структура самого компонента остаётся неизменной. Слот — это своего рода плейсхолдер, который может быть заменён содержимым, переданным родительским компонентом.

Пример базового использования слота:

@Component({
  tag: 'app-card',
  styleUrl: 'app-card.css',
  shadow: true
})
export class AppCard {
  render() {
    return (
      <div class="card">
        <slot></slot>
      </div>
    );
  }
}

В этом примере компонент app-card содержит слот, который может быть заполнен любым контентом при его использовании.

<app-card>
  <h1>Заголовок карточки</h1>
  <p>Это описание карточки.</p>
</app-card>

В результате содержимое внутри компонента app-card (в данном случае заголовок и параграф) будет вставлено в слот внутри компонента.

Множественные слоты

Stencil поддерживает использование нескольких слотов внутри одного компонента, что даёт ещё большую гибкость в проектировании интерфейсов. Каждый слот может быть именован для удобства, что позволяет точно указывать, куда должен быть вставлен контент.

Пример компонента с двумя слотами:

@Component({
  tag: 'app-layout',
  styleUrl: 'app-layout.css',
  shadow: true
})
export class AppLayout {
  render() {
    return (
      <div class="layout">
        <header>
          <slot name="header"></slot>
        </header>
        <main>
          <slot name="content"></slot>
        </main>
      </div>
    );
  }
}

Использование компонента с двумя слотами:

<app-layout>
  <div slot="header">
    <h1>Заголовок страницы</h1>
  </div>
  <div slot="content">
    <p>Основной контент страницы</p>
  </div>
</app-layout>

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

Слоты с дефолтным содержимым

Stencil также поддерживает возможность использования дефолтного содержимого для слотов, что позволяет задавать контент, который будет отображён, если слот не был заполнен родительским компонентом.

Пример компонента с дефолтным содержимым для слота:

@Component({
  tag: 'app-notification',
  styleUrl: 'app-notification.css',
  shadow: true
})
export class AppNotification {
  render() {
    return (
      <div class="notification">
        <slot>Нет сообщений</slot>
      </div>
    );
  }
}

В случае, если родительский компонент не передал контент в слот, будет отображено дефолтное сообщение «Нет сообщений».

<app-notification></app-notification>

Если в родительский компонент был передан контент для слота, дефолтное содержимое заменится этим контентом.

<app-notification>
  Новое сообщение
</app-notification>

Процесс рендеринга и взаимодействие с слотом

Stencil управляет рендерингом контента в слотах так же, как и в других фреймворках, но с учётом особенностей своего реактивного подхода. Когда слот заполняется или его содержимое изменяется, компонент автоматически перерендеривается, чтобы отразить эти изменения в интерфейсе пользователя. Этот процесс помогает обеспечить синхронность отображения данных с состоянием компонента.

Важно отметить, что Stencil использует Shadow DOM, что влияет на поведение слотов. Контент, переданный в слот, будет изолирован внутри компонента, и он не будет иметь прямого доступа к стилям или состоянию родительского компонента, если только родитель явно не передаст нужные стили или данные через свойства.

Управление несколькими слотами

Stencil также поддерживает работу с несколькими слотами внутри компонента, позволяя гибко настраивать компоненты с несколькими местами для вставки контента. Важно помнить, что порядок слотов в компоненте имеет значение. При отсутствии указания имени для слота, используется первый слот, который не имеет имени.

Пример компонента с несколькими слотами без указания имен:

@Component({
  tag: 'app-card',
  styleUrl: 'app-card.css',
  shadow: true
})
export class AppCard {
  render() {
    return (
      <div class="card">
        <slot></slot> {/* Первый слот */}
        <slot></slot> {/* Второй слот */}
      </div>
    );
  }
}

При использовании этого компонента родительский элемент может передавать контент для обоих слотов:

<app-card>
  <h1>Заголовок карточки</h1>
  <p>Описание карточки</p>
</app-card>

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

Усложнённые случаи

Слоты в Stencil могут быть использованы не только для простого вставления контента, но и для более сложных случаев, таких как использование компонентов внутри слотов. Это даёт возможность строить более сложные интерфейсы с взаимозависимыми компонентами.

Пример компонента с динамически изменяемым содержимым в слоте:

@Component({
  tag: 'app-accordion',
  styleUrl: 'app-accordion.css',
  shadow: true
})
export class AppAccordion {
  @State() isOpen: boolean = false;

  toggle() {
    this.isOpen = !this.isOpen;
  }

  render() {
    return (
      <div class="accordion">
        <button onCl ick={() => this.toggle()}>
          {this.isOpen ? 'Скрыть' : 'Показать'}
        </button>
        <div class={{'content': true, 'open': this.isOpen}}>
          <slot></slot>
        </div>
      </div>
    );
  }
}

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

Заключение

Использование слотов в Stencil — это мощный инструмент для создания компонентов с гибкой и переиспользуемой структурой. С помощью слотов можно легко интегрировать внешние элементы в компоненты, задавать дефолтное содержимое и даже управлять стилями и поведением в зависимости от контекста. Стратегия работы со слотами в Stencil значительно улучшает архитектуру компонентов, позволяя создавать динамичные, настраиваемые и независимые части интерфейса.