Композиция компонентов

Polymer — это библиотека для создания веб-компонентов, ориентированная на повторное использование элементов интерфейса и их композицию. Композиция компонентов позволяет строить сложные интерфейсы из небольших, независимых элементов, повышая модульность и упрощая поддержку кода.

Основы композиции

В Polymer каждый компонент — это отдельный класс, наследующийся от PolymerElement, с собственным шаблоном (template) и стилями. Композиция строится на принципе включения одного компонента в другой через HTML-разметку.

import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
import './child-element.js';

class ParentElement extends PolymerElement {
  static get template() {
    return html`
      <style>
        :host {
          display: block;
          padding: 16px;
          border: 1px solid #ccc;
        }
      </style>
      <child-element></child-element>
      <p>Дополнительный контент родителя</p>
    `;
  }
}

customElements.define('parent-element', ParentElement);

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

Вложенные компоненты и Shadow DOM

Polymer использует Shadow DOM, чтобы изолировать стили и структуру компонента. При композиции необходимо учитывать:

  • Инкапсуляция стилей: стили родителя не влияют на дочерние элементы, если не использовать специальные селекторы (::slotted).
  • Слоты для контента: <slot> позволяет родителю вставлять динамический контент внутрь компонента.

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

class CardElement extends PolymerElement {
  static get template() {
    return html`
      <style>
        :host {
          display: block;
          border: 1px solid #aaa;
          padding: 12px;
          border-radius: 4px;
        }
        ::slotted(*) {
          margin: 4px 0;
        }
      </style>
      <div>
        <slot></slot>
      </div>
    `;
  }
}

customElements.define('card-element', CardElement);

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

<card-element>
  <p>Текст внутри карточки</p>
  <button>Кнопка</button>
</card-element>

Связь между компонентами

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

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

Пример передачи данных и события:

class CounterElement extends PolymerElement {
  static get properties() {
    return {
      count: { type: Number, value: 0 }
    };
  }

  increment() {
    this.count += 1;
    this.dispatchEvent(new CustomEvent('count-changed', {
      detail: { count: this.count },
      bubbles: true,
      composed: true
    }));
  }

  static get template() {
    return html`
      <button on-click="increment">Увеличить</button>
      <p>Текущее значение: [[count]]</p>
    `;
  }
}

customElements.define('counter-element', CounterElement);

Родительский компонент может слушать событие count-changed и реагировать на изменения.

Динамическое добавление компонентов

Polymer позволяет создавать компоненты динамически через JavaScript, что особенно полезно для списков или интерфейсов с изменяющимся составом элементов:

const listContainer = document.querySelector('#list');
for (let i = 0; i < 5; i++) {
  const item = document.createElement('child-element');
  item.setAttribute('data-index', i);
  listContainer.appendChild(item);
}

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

Шаблоны и повторное использование

Polymer поддерживает шаблоны <dom-repeat> для генерации компонентов на основе массивов:

class ItemList extends PolymerElement {
  static get properties() {
    return {
      items: { type: Array, value: () => ['Элемент 1', 'Элемент 2', 'Элемент 3'] }
    };
  }

  static get template() {
    return html`
      <template is="dom-repeat" items="[[items]]">
        <child-element item="[[item]]"></child-element>
      </template>
    `;
  }
}

customElements.define('item-list', ItemList);

Преимущество: автоматически создаются и удаляются дочерние компоненты при изменении массива, сохраняя синхронизацию с данными.

Стилизация композиции

Для управления стилями вложенных компонентов применяются следующие подходы:

  • Использование слотов с селекторами ::slotted.
  • CSS-переменные для передачи значений из родителя в дочерний компонент.
  • Объединение глобальных и локальных стилей через <style include="shared-styles">.
<style>
  :host {
    --primary-color: #3498db;
  }
  child-element {
    color: var(--primary-color);
  }
</style>

Итоговые принципы композиции

  • Каждый компонент инкапсулирует свою логику и стиль.
  • Родитель и дочерний элемент взаимодействуют через свойства и события.
  • Динамическая генерация и шаблоны позволяют строить гибкие интерфейсы.
  • Слоты и CSS-переменные обеспечивают настройку внешнего вида и контента.

Комплексное использование этих механизмов делает Polymer мощным инструментом для создания модульных и легко поддерживаемых веб-приложений.