Haunted — это библиотека для создания веб-компонентов на базе Web Components с реактивным подходом, аналогичным React Hooks. Одним из ключевых механизмов является возможность определять компоненты через обычные функции, что позволяет создавать компактные и гибкие элементы интерфейса.
В Haunted компонент определяется как функция, возвращающая
шаблон HTML с использованием
html из haunted. Функция
может принимать свойства компонента через аргумент и
использовать hooks для управления состоянием и
побочными эффектами.
import { html, component } from 'haunted';
function MyCounter({ initial = 0 }) {
const [count, setCount] = useState(initial);
return html`
<div>
<p>Счётчик: ${count}</p>
<button @click=${() => setCount(count + 1)}>Увеличить</button>
</div>
`;
}
customElements.define('my-counter', component(MyCounter));
Ключевые моменты:
component(MyCounter) превращает обычную функцию в
полноценный веб-компонент.useState создаёт локальное состояние, доступное внутри
функции.{ initial } автоматически связывается
с атрибутами элемента.Функция-компонент может принимать свойства, которые автоматически синхронизируются с атрибутами DOM. Haunted автоматически конвертирует строки атрибутов в значения для свойства, если они соответствуют типам.
<my-counter initial="10"></my-counter>
Внутри функции значение initial будет числом
10, если определить тип свойства при
регистрации:
component(MyCounter, { observedAttributes: ['initial'] });
Функциональные компоненты Haunted поддерживают следующие основные хуки:
useState(initialValue) — создаёт
реактивное состояние.useEffect(effect, deps) — выполняет
побочный эффект при изменении зависимостей.useReducer(reducer, initialValue) —
аналог Redux-подхода для управления состоянием.useRef(initialValue) — создаёт ссылку
на DOM-элемент или значение, сохраняющееся между рендерами.useContext(context) — получение
контекста из родительского компонента.Пример с useEffect:
function Timer({ interval = 1000 }) {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const id = setInterval(() => setSeconds(s => s + 1), interval);
return () => clearInterval(id);
}, [interval]);
return html`<p>Прошло секунд: ${seconds}</p>`;
}
customElements.define('my-timer', component(Timer));
interval.[interval] определяют, когда
эффект будет перезапущен.Функциональные компоненты удобно комбинировать. Любой компонент можно использовать внутри другого, передавая свойства и слоты:
function Card({ title, children }) {
return html`
<div class="card">
<h3>${title}</h3>
<div>${children}</div>
</div>
`;
}
customElements.define('my-card', component(Card));
function App() {
return html`
<my-card title="Пример">
<p>Содержимое карточки</p>
</my-card>
`;
}
customElements.define('my-app', component(App));
children автоматически получает содержимое слота.События можно передавать через кастомные события, которые удобно использовать в функциональных компонентах:
function Button({ label }) {
const handleClick = () => dispatchEvent(new CustomEvent('action', { bubbles: true }));
return html`<button @click=${handleClick}>${label}</button>`;
}
customElements.define('my-button', component(Button));
function Toolbar() {
const handleAction = (e) => console.log('Кнопка нажата', e);
return html`<my-button label="Нажми" @action=${handleAction}></my-button>`;
}
customElements.define('my-toolbar', component(Toolbar));
@eventName=${handler}.bubbles: true позволяет событиям
всплывать до родительских компонентов.Haunted обеспечивает реактивность без ручного обновления
DOM. Любое изменение состояния через useState или
useReducer автоматически приводит к повторному рендеру
функции.
function Toggle() {
const [on, setOn] = useState(false);
return html`
<button @click=${() => setOn(!on)}>
${on ? 'Включено' : 'Выключено'}
</button>
`;
}
customElements.define('my-toggle', component(Toggle));
innerHTML или
shadowRoot.При регистрации компонента через component(func) можно
управлять использованием Shadow DOM:
customElements.define('my-shadow', component(MyComponent, { useShadowDOM: true }));
useShadowDOM: true создаёт изолированную область
стилей.useShadowDOM: false рендерит компонент в светлом DOM,
что удобно для интеграции с внешними стилями.Функциональная инициализация через функции в Haunted позволяет создавать реактивные, компактные и легко тестируемые веб-компоненты, которые полностью управляются состоянием, эффектами и событиями, минимизируя работу с низкоуровневым DOM и атрибутами.