Haunted — это библиотека для создания реактивных веб-компонентов на основе стандарта Web Components и функционального подхода. Она сочетает в себе удобство работы с хуками, аналогичными React, с легковесностью нативных компонентов. Основной принцип Haunted заключается в том, что компонент — это функция, которая возвращает шаблон HTML, управляемый реактивными хуками состояния и эффектов.
Компонент в Haunted определяется функцией и регистрируется через
customElements.define. Простейший пример:
import { html, component } from 'haunted';
function MyComponent() {
return html`<p>Привет, Haunted!</p>`;
}
customElements.define('my-component', component(MyComponent));
Ключевое здесь:
html — функция шаблонизатора для
создания шаблонов с реактивными привязками.component — обёртка, превращающая
обычную функцию в Web Component.Haunted использует хуки состояния, аналогичные React. Основной из них
— useState. Он позволяет хранить локальное состояние
компонента.
import { html, component, 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:
setCount шаблон
автоматически перерисовывается.Haunted предоставляет useEffect для управления побочными
эффектами, аналогично React. Он вызывается после рендера компонента.
import { html, component, useEffect, useState } 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:
Свойства (props) передаются как атрибуты в HTML. В Haunted они доступны как аргументы функции компонента:
function Greeting({ name }) {
return html`<p>Привет, ${name}!</p>`;
}
customElements.define('my-greeting', component(Greeting));
Особенности:
Haunted поддерживает нативные DOM-события. Привязка выполняется через
@имя_события:
function Clicker() {
const [count, setCount] = useState(0);
return html`
<button @click=${() => setCount(count + 1)}>
Кликнули ${count} раз
</button>
`;
}
customElements.define('my-clicker', component(Clicker));
Дополнительно можно использовать CustomEvent для
передачи событий родителю:
function Child() {
const handleClick = () => {
const event = new CustomEvent('child-click', {
detail: { message: 'Нажато!' },
bubbles: true,
composed: true
});
this.dispatchEvent(event);
};
return html`<button @click=${handleClick}>Нажми меня</button>`;
}
Haunted не навязывает систему стилей. Компоненты могут использовать:
<style> внутри
шаблона.Пример с Shadow DOM:
function StyledBox() {
return html`
<style>
div { color: white; background: blue; padding: 10px; }
</style>
<div>Стили применены!</div>
`;
}
customElements.define('styled-box', component(StyledBox));
Haunted поддерживает реактивное отображение списков через
map и динамическую генерацию элементов:
function TodoList({ todos }) {
return html`
<ul>
${todos.map(todo => html`<li>${todo}</li>`)}
</ul>
`;
}
customElements.define('todo-list', component(TodoList));
Важно:
Haunted поддерживает дополнительные хуки:
useReducer — для сложной логики
состояния.useRef — для доступа к
DOM-элементам.useMemo и
useCallback — для оптимизации вычислений и
функций.import { useReducer } from 'haunted';
function reducer(state, action) {
switch (action.type) {
case 'increment': return state + 1;
case 'decrement': return state - 1;
default: return state;
}
}
function Counter() {
const [count, dispatch] = useReducer(reducer, 0);
return html`
<button @click=${() => dispatch({ type: 'decrement' })}>-</button>
<span>${count}</span>
<button @click=${() => dispatch({ type: 'increment' })}>+</button>
`;
}
customElements.define('reducer-counter', component(Counter));
Haunted легко интегрируется с внешними библиотеками:
Haunted сочетает функциональный подход и нативные Web Components, обеспечивая реактивность без лишней сложности. Гибкая система хуков, декларативное управление состоянием и событиями делает её подходящей для современных веб-приложений, сохраняя лёгкость и минимальный размер кода.