Haunted — это библиотека для создания реактивных веб-компонентов на базе стандартных Web Components и Hooks, вдохновленных React. Основная идея заключается в том, чтобы комбинировать легковесность нативных кастомных элементов с мощью реактивного состояния и хуков.
Компонент в Haunted создается с помощью функции
component и стандартного класса HTMLElement.
Основная структура выглядит так:
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>
<button @click=${() => setCount(count - 1)}>Уменьшить</button>
</div>
`;
}
customElements.define('my-counter', component(Counter));
Ключевые моменты:
html используется для шаблонов с поддержкой реактивного
рендеринга.useState создает реактивное состояние внутри
компонента.@event=${handler}.Haunted реализует концепцию реактивности через хуки, аналогично React. Основные хуки:
Пример использования useEffect:
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => setSeconds(s => s + 1), 1000);
return () => clearInterval(interval);
}, []);
return html`<p>Прошло секунд: ${seconds}</p>`;
}
Важно помнить: эффект выполняется после рендера, а функция очистки вызывается перед следующим вызовом или при удалении компонента.
Haunted идеально подходит для гибридных приложений, где необходимо сочетание нативных веб-компонентов с реактивными элементами. Примеры гибридного подхода:
Haunted-компоненты можно монтировать динамически в любой элемент страницы:
import { component, html } from 'haunted';
function Hello({ name }) {
return html`<p>Привет, ${name}!</p>`;
}
const HelloComponent = component(Hello);
customElements.define('hello-component', HelloComponent);
document.querySelector('#app').appendChild(document.createElement('hello-component'));
document.querySelector('hello-component').name = 'Мир';
Это позволяет постепенно внедрять реактивность в большие проекты без полной переработки существующей архитектуры.
Использование useReducer удобно для гибридных приложений
с большим количеством состояний:
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 Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return html`
<div>
<p>Счётчик: ${state.count}</p>
<button @click=${() => dispatch({ type: 'increment' })}>+</button>
<button @click=${() => dispatch({ type: 'decrement' })}>-</button>
</div>
`;
}
Этот подход позволяет централизованно управлять состояниями, что особенно полезно при взаимодействии нескольких компонентов.
Haunted можно использовать вместе с популярными UI-библиотеками:
useEffect.Пример динамического графика:
function ChartComponent({ data }) {
const chartRef = useRef(null);
useEffect(() => {
const chart = new Chart(chartRef.current, { type: 'bar', data });
return () => chart.destroy();
}, [data]);
return html`<canvas ${ref(chartRef)}></canvas>`;
}
Компоненты Haunted могут общаться через:
dispatchEvent/addEventListener).Пример кастомного события:
function Child() {
return html`<button @click=${() => this.dispatchEvent(new CustomEvent('say-hi', { bubbles: true }))}>Сказать привет</button>`;
}
function Parent() {
useEffect(() => {
const handleHi = (e) => console.log('Привет от дочернего компонента');
this.addEventListener('say-hi', handleHi);
return () => this.removeEventListener('say-hi', handleHi);
}, []);
return html`<child-component></child-component>`;
}
customElements.define('child-component', component(Child));
customElements.define('parent-component', component(Parent));
Это обеспечивает гибкую, модульную архитектуру даже в больших приложениях.
useEffect и useMemo для
предотвращения лишних рендеров.html-шаблонов.Haunted предоставляет мощный инструмент для построения современных гибридных приложений, где реактивные компоненты и нативные элементы работают в едином потоке, обеспечивая высокую производительность и легкость поддержки.