Интеграция с Web Components

Hyperapp представляет собой минималистичный JavaScript-фреймворк для построения UI на основе функционального подхода с использованием виртуального DOM. Одним из важных аспектов современного фронтенд-разработки является возможность интеграции с Web Components — нативной технологией браузеров для создания инкапсулированных, переиспользуемых компонентов.

Основы Web Components

Web Components включают три ключевых технологии:

  1. Custom Elements — создание собственных HTML-тегов с определённой логикой.
  2. Shadow DOM — инкапсуляция DOM и стилей, предотвращающая их влияние на внешнюю страницу.
  3. HTML Templates — шаблоны для многократного использования разметки без её немедленного рендеринга.

Hyperapp может взаимодействовать с Web Components через обычные HTML-элементы, так как виртуальный DOM Hyperapp поддерживает любые кастомные теги.

Использование кастомных элементов в Hyperapp

Создание и регистрация Web Component:

class MyCounter extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.count = 0;
    this.render();
  }

  increment() {
    this.count += 1;
    this.render();
  }

  render() {
    this.shadowRoot.innerHTML = `
      <button id="btn">Count: ${this.count}</button>
    `;
    this.shadowRoot.querySelector('#btn').oncl ick = () => this.increment();
  }
}

customElements.define('my-counter', MyCounter);

Интеграция с Hyperapp сводится к использованию тега <my-counter /> внутри функции view:

import { h, app } from "hyperapp";

const state = {};

const actions = {};

const view = () => (
  h("div", {}, [
    h("h1", {}, "Hyperapp + Web Components"),
    h("my-counter", {})
  ])
);

app({ init: state, view, node: document.getElementById("app"), actions });

Ключевой момент: Hyperapp не конфликтует с Shadow DOM и просто рендерит кастомный тег в виртуальный DOM. Вся логика компонента остаётся внутри самого Web Component.

Передача данных и событий

Hyperapp позволяет работать с атрибутами и событиями кастомных элементов. Для передачи данных можно использовать стандартные HTML-атрибуты:

h("my-counter", { initialCount: 5 })

Внутри Web Component можно получать атрибут через this.getAttribute('initialCount').

События можно обрабатывать через стандартные слушатели:

class MyCounter extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.count = Number(this.getAttribute('initialCount') || 0);
    this.render();
  }

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

  render() {
    this.shadowRoot.innerHTML = `
      <button id="btn">Count: ${this.count}</button>
    `;
    this.shadowRoot.querySelector('#btn').oncl ick = () => this.increment();
  }
}

В Hyperapp можно подписаться на событие:

const view = () => (
  h("my-counter", {
    initialCount: 2,
    oncountchanged: e => console.log("Новое значение:", e.detail)
  })
);

Hyperapp автоматически нормализует имена событий и позволяет использовать их как свойства элемента.

Динамическая работа с Web Components

Hyperapp поддерживает динамическое создание и удаление кастомных элементов. Любое состояние Hyperapp может напрямую изменять свойства или атрибуты Web Component через виртуальный DOM:

const state = { initial: 0 };

const actions = {
  increment: s => ({ initial: s.initial + 1 })
};

const view = (state, actions) => (
  h("div", {}, [
    h("my-counter", { initialCount: state.initial }),
    h("button", { onclick: actions.increment }, "Изменить начальное значение")
  ])
);

При изменении состояния Hyperapp обновляет атрибут initialCount у Web Component, что позволяет синхронизировать UI без прямого вмешательства в DOM.

Стилизация Web Components

Стили внутри Shadow DOM не влияют на внешние элементы. Для глобальной стилизации можно использовать CSS Custom Properties, которые передаются через атрибуты:

my-counter {
  --button-bg: red;
}

my-counter button {
  background-color: var(--button-bg);
}

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

Особенности и ограничения

  • Web Components не поддерживают двустороннюю привязку из коробки; необходимо использовать события и атрибуты.
  • Hyperapp не рендерит содержимое Shadow DOM, но корректно управляет самим элементом.
  • При большом количестве динамических Web Components важно учитывать производительность, так как каждый Shadow DOM создаёт отдельный контекст рендеринга.

Примеры сложной интеграции

Комбинирование нескольких Web Components с Hyperapp позволяет строить модульные интерфейсы. Например, список карточек с кастомными элементами:

const cards = ["card-one", "card-two", "card-three"];

const view = () => (
  h("div", {}, cards.map(name => h(name, { title: name })))
);

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


Hyperapp и Web Components создают мощный дуэт для построения масштабируемых UI с полной инкапсуляцией компонентов и гибкой реактивной логикой. Такой подход обеспечивает чистую архитектуру и возможность постепенной миграции существующего кода на современные стандарты фронтенда.