Haunted — это минималистичная библиотека для работы с Web Components, построенная на базе Custom Elements API и вдохновлённая React Hooks. Она позволяет создавать реактивные компоненты с использованием привычных хуков, при этом полностью опираясь на стандарт Web Components.
В Haunted каждый компонент создаётся как функция, возвращающая шаблон
разметки с помощью html из библиотеки. Для интеграции с
браузером используется стандартный метод
customElements.define.
Пример:
import { html, render } from 'haunted';
function MyComponent({ name }) {
return html`<p>Hello, ${name}!</p>`;
}
customElements.define('my-component', haunted(MyComponent));
Ключевые моменты:
haunted() — оборачивает функцию в Web
Component, автоматически управляя состоянием и рендером.Haunted автоматически связывает свойства и атрибуты элемента:
function Greeting({ message }) {
return html`<h1>${message}</h1>`;
}
customElements.define('greeting-element', haunted(Greeting));
message="Привет" на
<greeting-element> будет доступен в качестве свойства
message.const elem = document.createElement('greeting-element');
elem.message = 'Добро пожаловать';
document.body.appendChild(elem);
Haunted поддерживает множество хуков, аналогичных React, для управления состоянием и побочными эффектами. Основные из них:
import { useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<button @click=${() => setCount(count + 1)}>
Count: ${count}
</button>
`;
}
setCount вызывает повторный рендер компонента
автоматически.import { useEffect } from 'haunted';
function Timer() {
const [time, setTime] = useState(0);
useEffect(() => {
const id = setInterval(() => setTime(t => t + 1), 1000);
return () => clearInterval(id); // очистка при размонтировании
}, []); // пустой массив: эффект выполняется один раз
}
Haunted полностью поддерживает Shadow DOM и слоты, что позволяет создавать изолированные компоненты с возможностью вставки пользовательского контента:
function Card() {
return html`
<div class="card">
<slot name="header"></slot>
<slot></slot>
</div>
`;
}
customElements.define('my-card', haunted(Card));
<slot> позволяет размещать контент внутри
компонента.name на слоте соответствует атрибуту
slot у вложенных элементов.События внутри Haunted-компонентов связываются через
@event синтаксис:
function Clicker() {
const [count, setCount] = useState(0);
return html`
<button @click=${() => setCount(count + 1)}>
Нажато ${count} раз
</button>
`;
}
@click — синтаксический сахар для
addEventListener('click', ...).Haunted позволяет расширять существующие элементы и создавать сложные компоненты через стандартные механизмы Web Components:
class FancyButton extends HTMLButtonElement {
connectedCallback() {
this.style.color = 'red';
}
}
customElements.define('fancy-button', FancyButton, { extends: 'button' });
extends позволяет создавать кастомные элементы на
основе нативных тегов.haunted().Ключевая особенность Haunted — реактивность без виртуального DOM:
setState инициирует рендер только текущего
элемента.customElements.define.@event.Haunted сочетает преимущества Web Components и современного реактивного подхода через хуки, позволяя писать компактный, предсказуемый и эффективный код.