Вложенные компоненты

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

В Haunted компонент определяется как обычная JavaScript-функция, возвращающая разметку с использованием html из haunted. Вложенные компоненты могут быть локальными, определёнными внутри родительского компонента, или глобальными, зарегистрированными как отдельные кастомные элементы.


Создание локальных вложенных компонентов

Локальные компоненты создаются прямо внутри функции родителя. Это удобно для компонентов, которые не будут использоваться повторно в других местах.

import { html, component } from 'haunted';

function ParentComponent() {
  function ChildComponent({ text }) {
    return html`<p>${text}</p>`;
  }

  return html`
    <div>
      <h2>Родительский компонент</h2>
      ${ChildComponent({ text: 'Это дочерний компонент' })}
    </div>
  `;
}

customElements.define('parent-component', component(ParentComponent));

Особенности локальных компонентов:

  • Передача пропсов осуществляется через объект аргументов.
  • Локальный компонент не регистрируется в DOM как отдельный тег, это обычная функция.
  • Можно создавать несколько вложенных компонентов внутри родителя с различной логикой.

Глобальные вложенные компоненты

Глобальные компоненты регистрируются через customElements.define, что позволяет использовать их как стандартные HTML-теги.

function ChildComponent({ message }) {
  return html`<span>${message}</span>`;
}

customElements.define('child-component', component(ChildComponent));

function ParentComponent() {
  return html`
    <div>
      <h2>Родительский компонент</h2>
      <child-component .message=${'Сообщение из родителя'}></child-component>
    </div>
  `;
}

customElements.define('parent-component', component(ParentComponent));

Преимущества:

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

Передача данных между компонентами

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

import { html, component, useState } from 'haunted';

function ChildComponent({ count }) {
  return html`<p>Счётчик: ${count}</p>`;
}

customElements.define('child-component', component(ChildComponent));

function ParentComponent() {
  const [count, setCount] = useState(0);

  return html`
    <div>
      <button @click=${() => setCount(count + 1)}>Увеличить</button>
      <child-component .count=${count}></child-component>
    </div>
  `;
}

customElements.define('parent-component', component(ParentComponent));

Ключевые моменты:

  • Свойства передаются через . перед именем атрибута (.count=${count}), чтобы передавать объект или число, а не строку.
  • Изменение состояния useState автоматически обновляет все зависимости в разметке, включая вложенные компоненты.

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

В Haunted часто используют композицию, объединяя несколько маленьких компонентов в один крупный. Это повышает читаемость и модульность кода.

function Header({ title }) {
  return html`<h1>${title}</h1>`;
}

function Footer({ year }) {
  return html`<footer>© ${year}</footer>`;
}

function Page() {
  return html`
    <div>
      ${Header({ title: 'Главная страница' })}
      <main>Основной контент</main>
      ${Footer({ year: new Date().getFullYear() })}
    </div>
  `;
}

customElements.define('page-component', component(Page));

Преимущества композиции:

  • Разделение логики на независимые части.
  • Упрощение тестирования и отладки.
  • Возможность переиспользовать отдельные компоненты в других проектах.

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

Каждый компонент Haunted создаёт свой Shadow DOM по умолчанию. Это означает:

  • Стили родителя не влияют на дочерний компонент.
  • Дочерний компонент может иметь собственные CSS без конфликтов.
  • Изоляция состояния предотвращает непреднамеренные побочные эффекты.
function Child() {
  return html`<p style="color:red;">Красный текст</p>`;
}

customElements.define('child-element', component(Child));

function Parent() {
  return html`
    <div>
      <p style="color:blue;">Синий текст родителя</p>
      <child-element></child-element>
    </div>
  `;
}

customElements.define('parent-element', component(Parent));

В этом примере текст родителя синий, а дочерний компонент — красный, без конфликтов CSS.


Управление событиями между компонентами

Haunted позволяет отправлять события из дочернего компонента родителю через стандартный механизм CustomEvent:

function Child({ onClick }) {
  return html`<button @click=${onClick}>Нажми меня</button>`;
}

customElements.define('child-component', component(Child));

function Parent() {
  const handleClick = () => alert('Событие от дочернего компонента');
  return html`<child-component .onCl ick=${handleClick}></child-component>`;
}

customElements.define('parent-component', component(Parent));

Особенности:

  • Можно использовать CustomEvent для более сложных случаев.
  • Обработка событий полностью совместима с Shadow DOM.
  • Позволяет создавать интерактивные вложенные компоненты с реактивным состоянием.

Итоги использования вложенных компонентов

  • Локальные компоненты подходят для внутренней логики, глобальные — для переиспользуемых.
  • Использование useState и реактивных свойств позволяет динамически обновлять вложенные компоненты.
  • Shadow DOM обеспечивает изоляцию стилей и состояния.
  • События и пропсы позволяют родителю управлять дочерними компонентами и получать обратную связь.
  • Композиция упрощает архитектуру крупных интерфейсов, повышает модульность и повторное использование кода.

В Haunted вложенные компоненты становятся фундаментальным инструментом для построения сложных, реактивных и поддерживаемых интерфейсов, сохраняя при этом чистоту и функциональный стиль кода.