Slots и композиция контента

Stencil предоставляет механизм, известный как слоты (Slots), который позволяет вставлять динамический контент в компоненты. Этот механизм является важной частью архитектуры компонентов и помогает создавать гибкие, переиспользуемые и расширяемые компоненты. Слоты в Stencil похожи на аналогичную концепцию в других фреймворках, таких как Web Components или Vue, но имеют свои особенности.

Что такое слот в Stencil?

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

Слоты в Stencil работают на основе стандартных Web Components. Весь механизм построен на Shadow DOM, что обеспечивает инкапсуляцию и отделение стилей и структуры компонента от внешнего контента.

Основы использования слотов

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

import { Component, h } from '@stencil/core';

@Component({
  tag: 'my-component',
  shadow: true
})
export class MyComponent {
  render() {
    return (
      <div>
        <h1>Мой компонент</h1>
        <slot></slot> {/* Здесь будет отображаться переданный контент */}
      </div>
    );
  }
}

В этом примере контент, переданный в <my-component>, будет отображаться в месте, где размещён тег <slot>. Если в компонент не передан контент для слота, то слот останется пустым, и не будет отображено ничего.

<my-component>
  <p>Этот текст будет отображён в слоте.</p>
</my-component>

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

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

Чтобы создать именованный слот, необходимо указать атрибут name у тега <slot>. Пример:

import { Component, h } from '@stencil/core';

@Component({
  tag: 'my-component',
  shadow: true
})
export class MyComponent {
  render() {
    return (
      <div>
        <header>
          <slot name="header"></slot> {/* Именованный слот для шапки */}
        </header>
        <main>
          <slot></slot> {/* Стандартный слот для основного контента */}
        </main>
        <footer>
          <slot name="footer"></slot> {/* Именованный слот для подвала */}
        </footer>
      </div>
    );
  }
}

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

<my-component>
  <div slot="header">Это шапка</div>
  <div>Основной контент</div>
  <div slot="footer">Это подвал</div>
</my-component>

Текст «Основной контент» будет вставлен в стандартный слот, а остальные элементы — в именованные слоты.

Резервный контент (Fallback content)

В случае, если для слота не был передан контент, можно указать резервный контент. Этот контент будет отображён, если слот пустой. Резервный контент размещается внутри тега <slot> и является альтернативой на случай, если слот не заполнен.

import { Component, h } from '@stencil/core';

@Component({
  tag: 'my-component',
  shadow: true
})
export class MyComponent {
  render() {
    return (
      <div>
        <slot>
          <p>Здесь будет отображаться запасной контент</p>
        </slot>
      </div>
    );
  }
}

Если слот не содержит контента, будет отображён параграф с текстом «Здесь будет отображаться запасной контент».

Использование слотов с компонентами

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

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

import { Component, h } from '@stencil/core';

@Component({
  tag: 'custom-card',
  shadow: true
})
export class CustomCard {
  render() {
    return (
      <div class="card">
        <header>
          <slot name="title"></slot> {/* Название карточки */}
        </header>
        <section>
          <slot></slot> {/* Основное содержание карточки */}
        </section>
        <footer>
          <slot name="footer"></slot> {/* Подвал карточки */}
        </footer>
      </div>
    );
  }
}
<custom-card>
  <div slot="title">Заголовок карточки</div>
  <p>Контент карточки...</p>
  <div slot="footer">Подвал карточки</div>
</custom-card>

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

Слоты с несколькими уровнями вложенности

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

Пример:

@Component({
  tag: 'parent-component',
  shadow: true
})
export class ParentComponent {
  render() {
    return (
      <div>
        <child-component>
          <div slot="child">Контент для дочернего компонента</div>
        </child-component>
      </div>
    );
  }
}

И дочерний компонент:

@Component({
  tag: 'child-component',
  shadow: true
})
export class ChildComponent {
  render() {
    return (
      <div>
        <slot name="child"></slot> {/* Здесь будет контент, переданный из родителя */}
      </div>
    );
  }
}

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

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

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

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

Заключение

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