Hyperapp представляет собой минималистичный JavaScript-фреймворк для построения UI на основе функционального подхода с использованием виртуального DOM. Одним из важных аспектов современного фронтенд-разработки является возможность интеграции с Web Components — нативной технологией браузеров для создания инкапсулированных, переиспользуемых компонентов.
Web Components включают три ключевых технологии:
Hyperapp может взаимодействовать с Web Components через обычные HTML-элементы, так как виртуальный DOM 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 автоматически нормализует имена событий и позволяет использовать их как свойства элемента.
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.
Стили внутри Shadow DOM не влияют на внешние элементы. Для глобальной стилизации можно использовать CSS Custom Properties, которые передаются через атрибуты:
my-counter {
--button-bg: red;
}
my-counter button {
background-color: var(--button-bg);
}
Это позволяет централизованно управлять внешним видом компонентов, сохраняя инкапсуляцию логики.
Комбинирование нескольких 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 с полной инкапсуляцией компонентов и гибкой реактивной логикой. Такой подход обеспечивает чистую архитектуру и возможность постепенной миграции существующего кода на современные стандарты фронтенда.