Библиотека Haunted представляет собой тонкий слой над Web Components, позволяющий использовать реактивные хуки из экосистемы React для построения компонент без громоздкой инфраструктуры. Синтаксис следует идеям функционального программирования: функция описывает рендер, состояние управляется хуками, а компонент объявляется однократно и затем используется в разметке как нативный элемент.
Haunted использует функцию component для обёртывания
рендера и регистрации Web Component:
import { component, useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<button @click=${() => setCount(count + 1)}>${count}</button>
`;
}
customElements.define('my-counter', component(Counter));
Ключевые особенности синтаксиса:
• Рендер представлен функцией без класса. • Внутри возможны хуки
useState, useEffect, useMemo и
другие. • Возврат осуществляется шаблонной строкой html,
предоставляемой библиотекой lit-html. • Регистрация в
customElements.define делает компонент доступным как
HTML-элемент.
Haunted не рендерит HTML напрямую. Для шаблонов требуется
lit-html или его наследник lit. Из коробки
используется lit-html, поэтому важно убедиться в наличии
соответствующего пакета:
npm install haunted lit-html
При использовании актуальной линейки Lit зависимость меняется следующим образом:
npm install haunted lit
Импорт шаблонов в актуальной версии Lit:
import { html } from 'lit';
В старой версии:
import { html } from 'lit-html';
Внешние стили не навязываются: стилизация может выполняться через Shadow DOM, global CSS или CSS-in-JS. Haunted лишь создаёт реактивный рендер-функциональный слой, не вмешиваясь в выбор стека.
Хуки в Haunted полностью вдохновлены архитектурой React, при этом синтаксис идентичен большинству случаев:
import { useState, useEffect } from 'haunted';
useState хранит локальное состояние компонента,
useEffect выполняет побочные действия с опциональной
очисткой, useMemo и useCallback оптимизируют
вычисления. Основополагающее правило: количество и порядок хуков в
функции должны оставаться стабильными, иначе внутренний менеджер
состояния нарушит соответствие индексов.
Пример использования эффектов:
function Timer() {
const [t, setT] = useState(0);
useEffect(() => {
const id = setInterval(() => setT(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return html`${t}`;
}
componentФункция component связывает рендер-функцию с жизненным
циклом Web Components: создаёт Shadow DOM (по умолчанию), подключает
автообновление при изменении состояния и обеспечивает корректную очистку
эффектов при удалении узла из DOM.
Допустимые аргументы фабрики:
component(render, { useShadowDOM: true })
Опция useShadowDOM отключается при необходимости
взаимодействия с глобальными стилями или фреймворками, рендерящими в
Light DOM.
Web Components ожидают передачи данных либо атрибутами, либо через JS-свойства. Haunted принимает оба варианта. При использовании атрибутов значения сериализуются в строки, поэтому для сложных данных предпочтительнее свойства:
el.items = [{ id: 1 }, { id: 2 }];
Чтобы синхронизировать атрибуты и свойства, можно добавить
observedAttributes через сторонние паттерны или
вспомогательные утилиты. Haunted здесь не вводит дополнительной магии,
сохраняя контроль на уровне платформы.
Haunted не требует сборщика, но чаще применяется в связке с Rollup, Vite или Webpack. Это упрощает использование современных импортов и tree shaking. Корректный таргет — современные браузеры с поддержкой Custom Elements, Shadow DOM и ES Modules. Для старых браузеров требуется набор полифилов:
• Custom Elements • Shadow DOM • ES Modules или SystemJS
Полифилы не включены в библиотеку намеренно, чтобы не увеличивать размер бандла и оставить выбор разработчику.
Haunted органично работает с любым инструментом, который понимает Web Components: • Lit (для шаблонов и директив) • Storybook (для документации) • Design Systems (как часть UI-библиотек) • Фреймворки, поддерживающие внешние Web Components (Vue, Svelte, Angular)
Отсутствие собственного DOM-диффа и логики синхронизации делает Haunted минималистичной прослойкой, обеспечивающей реактивное состояние и жизненный цикл поверх нативной платформы.