Haunted — это библиотека для создания веб-компонентов на базе
стандартов Web Components с реактивностью, вдохновлённой React Hooks.
Центральным элементом Haunted является функция component,
которая преобразует обычную функцию в полноценный кастомный элемент. Она
объединяет декларативный подход и возможности реактивного обновления
интерфейса.
Функция component принимает на вход функцию
рендеринга, которая возвращает разметку в формате
html из библиотеки lit-html. Сигнатура
выглядит следующим образом:
import { component, html } from 'haunted';
function MyComponent({ name }) {
return html`<p>Hello, ${name}!</p>`;
}
customElements.define('my-component', component(MyComponent));
MyComponent — обычная
JavaScript-функция, принимающая объект с пропсами.component(MyComponent) — возвращает
класс кастомного элемента, который можно зарегистрировать через
customElements.define.html из
lit-html, что обеспечивает эффективное обновление DOM.Любые атрибуты, заданные в HTML, автоматически преобразуются в пропсы. Haunted использует реактивную привязку пропсов, что позволяет компоненту автоматически реагировать на их изменения.
<my-component name="Alice"></my-component>
Внутри функции:
function MyComponent({ name }) {
return html`<p>Hello, ${name}!</p>`;
}
При изменении атрибута name на
<my-component name="Bob"></my-component>
компонент будет автоматически обновлён.
useStateHaunted интегрирует хуки, аналогичные React. useState
позволяет создавать локальное состояние компонента:
import { useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<p>Count: ${count}</p>
<button @click=${() => setCount(count + 1)}>Increment</button>
`;
}
customElements.define('my-counter', component(Counter));
useState возвращает массив
[значение, функция для обновления].setCount автоматически
инициирует повторный рендер компонента.Haunted предоставляет хуки для управления жизненным циклом:
useEffect — выполняет побочные
эффекты после рендера.
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>`;
}useRef — создаёт ссылку на
DOM-элемент:
import { useRef } from 'haunted';
function InputFocus() {
const inputRef = useRef();
return html`
<input ${ref(inputRef)} />
<button @click=${() => inputRef.current.focus()}>Focus</button>
`;
}useContext и
provide — для управления глобальными
данными в дереве компонентов.
Haunted использует синтаксис lit-html для привязки
событий через @event:
<button @click=${handleClick}>Click me</button>
handleClick может изменять состояние через хуки
useState.Компоненты Haunted автоматически перерендериваются
при изменении состояния или пропсов. Нет необходимости вручную обновлять
DOM или использовать setState как в обычном Web Components.
Ключевые моменты:
lit-html, что
повышает производительность.function TodoList({ initialTodos = [] }) {
const [todos, setTodos] = useState(initialTodos);
const [newTodo, setNewTodo] = useState('');
const addTodo = () => {
if (newTodo.trim()) {
setTodos([...todos, newTodo.trim()]);
setNewTodo('');
}
};
return html`
<ul>
${todos.map(todo => html`<li>${todo}</li>`)}
</ul>
<input
type="text"
.value=${newTodo}
@input=${e => setNewTodo(e.target.value)}
/>
<button @click=${addTodo}>Add</button>
`;
}
customElements.define('todo-list', component(TodoList));
componentФункция component поддерживает второй аргумент — объект
настроек:
component(MyComponent, { useShadowDOM: true, observedAttributes: ['name'] });
useShadowDOM — включает Shadow DOM для
изоляции стилей.observedAttributes — позволяет
указать, какие атрибуты должны отслеживаться для перерендеринга.Функция component в Haunted является сердцем реактивной
архитектуры. Она объединяет декларативный рендеринг, хуки состояния и
жизненного цикла, а также эффективное обновление DOM, создавая мощный
инструмент для построения современных веб-компонентов.