Haunted — это легковесная библиотека для создания веб-компонентов на базе Web Components и Hooks, вдохновлённая подходом React. Она позволяет использовать функциональный стиль разработки с состоянием и эффектами, сохраняя нативную совместимость с браузером. Главная особенность Haunted заключается в том, что компоненты остаются стандартными HTML-элементами, а логика состояния управляется через хуки.
Компонент в Haunted определяется как функция, которая возвращает
разметку через lit-html шаблоны. Ключевой инструмент —
функция component из библиотеки.
import { component, html, useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<div>
<p>Счётчик: ${count}</p>
<button @click=${() => setCount(count + 1)}>Увеличить</button>
</div>
`;
}
customElements.define('my-counter', component(Counter));
Пояснения:
useState — хук состояния. Возвращает текущее значение и
функцию для его обновления.html — функция шаблона, которая создаёт реактивную
разметку.@click — синтаксис для привязки событий в Haunted (на
основе lit-html).Состояние компонента в Haunted полностью реактивное. Изменение
значения через setState вызывает повторный
рендер компонента. Можно использовать несколько состояний
одновременно:
function MultiCounter() {
const [countA, setCountA] = useState(0);
const [countB, setCountB] = useState(0);
return html`
<div>
<p>A: ${countA}</p>
<p>B: ${countB}</p>
<button @click=${() => setCountA(countA + 1)}>Увеличить A</button>
<button @click=${() => setCountB(countB + 1)}>Увеличить B</button>
</div>
`;
}
customElements.define('multi-counter', component(MultiCounter));
Хуки эффектов (useEffect) позволяют выполнять побочные
действия, аналогично React. Основные сценарии: подписки, таймеры,
запросы к API.
import { useEffect } from 'haunted';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => setSeconds(s => s + 1), 1000);
return () => clearInterval(interval); // Очистка при размонтировании
}, []); // Пустой массив зависимостей — эффект выполняется один раз
return html`<p>Прошло секунд: ${seconds}</p>`;
}
customElements.define('my-timer', component(Timer));
Ключевые моменты:
useEffect, используется для
очистки ресурсов.Haunted поддерживает реактивные свойства (props), которые передаются через атрибуты HTML.
function Greeting({ name }) {
return html`<p>Привет, ${name}!</p>`;
}
customElements.define('my-greeting', component(Greeting));
Использование:
<my-greeting name="Алексей"></my-greeting>
Особенности:
document.querySelector('my-greeting').name = { first: 'Алексей', last: 'Иванов' };
Haunted предоставляет несколько стандартных хуков:
useReducer — управление состоянием через редьюсер,
полезно при сложной логике.useRef — хранение ссылки на DOM-элемент между
рендерами.useContext — создание контекста и совместное
использование состояния между компонентами.Пример useReducer:
import { useReducer } from 'haunted';
function reducer(state, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'decrement': return { count: state.count - 1 };
default: return state;
}
}
function ReducerCounter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return html`
<p>Счётчик: ${state.count}</p>
<button @click=${() => dispatch({ type: 'increment' })}>+</button>
<button @click=${() => dispatch({ type: 'decrement' })}>-</button>
`;
}
customElements.define('reducer-counter', component(ReducerCounter));
Haunted совместим с любыми библиотеками, которые работают с нативными
DOM-элементами. Можно легко подключать стили через CSS-in-JS или
lit-element шаблоны, а также использовать сторонние
UI-компоненты.
Пример интеграции с кастомным элементом:
function FancyButton({ label }) {
return html`<button class="fancy">${label}</button>`;
}
customElements.define('fancy-button', component(FancyButton));
function App() {
return html`<fancy-button label="Нажми меня"></fancy-button>`;
}
customElements.define('app-root', component(App));
lit-html.Haunted идеально подходит для постепенного внедрения в существующие проекты. Можно добавлять новые компоненты по мере необходимости, оставляя остальной код на чистом HTML/JS. Компоненты остаются совместимыми с любыми браузерами, поддерживающими Web Components, что позволяет интегрировать Haunted в проект без полной переработки архитектуры.