Haunted — это библиотека для создания веб-компонентов с использованием реактивного подхода, вдохновлённого React Hooks. Основой любого приложения на Haunted являются кастомные элементы, которые требуют корректной регистрации в браузере. Процесс регистрации обеспечивает связь между определением компонента и его использованием в DOM.
В Haunted компонент создаётся с помощью функции
component. Она принимает функцию с состояниями и возвращает
HTML-шаблон, создаваемый через html из
lit-html.
Пример базового компонента:
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>
</div>
`;
}
Ключевым моментом является то, что функция компонента должна быть чистой, без прямого взаимодействия с DOM, кроме возвращаемого шаблона.
customElements.defineДля того чтобы браузер распознавал компонент как тег HTML, необходимо зарегистрировать его с помощью стандартного API Web Components:
customElements.define('my-counter', component(Counter));
Аргументы:
'my-counter' — имя нового кастомного элемента. Должно
содержать хотя бы один дефис.component(Counter) — результат вызова
component, который превращает функцию в полноценный
веб-компонент.После регистрации <my-counter></my-counter>
можно использовать в любом месте DOM.
componentФункция component принимает второй необязательный
аргумент — опции компонента:
component(fn, { observedAttributes, useShadowDOM = true })
observedAttributes — массив атрибутов, на изменения
которых компонент должен реагировать. Атрибуты автоматически передаются
в функцию компонента.useShadowDOM — логическое значение, определяющее
использование Shadow DOM (по умолчанию true).Пример с атрибутами:
function Greeting({ name }) {
return html`<p>Привет, ${name}!</p>`;
}
customElements.define(
'my-greeting',
component(Greeting, { observedAttributes: ['name'] })
);
Теперь изменение атрибута name у элемента
<my-greeting> автоматически обновит компонент:
const greeting = document.querySelector('my-greeting');
greeting.setAttribute('name', 'Алексей');
По умолчанию Haunted компоненты используют Shadow DOM, что изолирует стили компонента от глобальных CSS.
Если нужно отключить Shadow DOM, например для интеграции с существующей разметкой:
customElements.define(
'no-shadow-component',
component(MyComponent, { useShadowDOM: false })
);
В этом случае все стили наследуются из внешнего контекста, и компонент полностью рендерится в light DOM.
Haunted автоматически отслеживает изменения состояния через
useState, но также можно использовать атрибуты и
свойства элемента.
Пример с синхронизацией атрибутов и состояния:
function Toggle({ active }) {
const [isActive, setActive] = useState(active === 'true');
return html`
<button @click=${() => setActive(!isActive)}>
${isActive ? 'Включено' : 'Выключено'}
</button>
`;
}
customElements.define(
'my-toggle',
component(Toggle, { observedAttributes: ['active'] })
);
Изменение атрибута active обновляет состояние
компонента, а взаимодействие пользователя изменяет состояние и при
желании может синхронизироваться с атрибутом.
Компоненты можно регистрировать условно или динамически, проверяя наличие уже существующего элемента:
if (!customElements.get('dynamic-counter')) {
customElements.define('dynamic-counter', component(Counter));
}
Это предотвращает ошибки повторной регистрации, особенно в крупных приложениях или при модульной загрузке кода.
html из
Haunted.useShadowDOM.observedAttributes, автоматически
отслеживаются и передаются компоненту.Регистрация компонента — фундаментальный шаг для работы с Haunted, от которого зависит корректная реактивность, изоляция стилей и возможность повторного использования в любом месте приложения.