Haunted — это современная библиотека для создания веб-компонентов с использованием функционального подхода и реактивного состояния, построенная на стандартах Web Components. Она позволяет писать компоненты, используя привычный синтаксис React-хуков, но при этом генерирует нативные кастомные элементы, полностью совместимые с любыми браузерами.
Основная цель Haunted — сделать веб-компоненты легковесными, реактивными и модульными, сохраняя преимущества стандартов браузера без дополнительного виртуального DOM.
Haunted можно подключить несколькими способами:
npm install haunted
<script type="module">
import { html, component, useState, useEffect } from 'https://unpkg.com/haunted/haunted.js?module';
</script>
После подключения доступны следующие ключевые функции:
html, component, useState,
useEffect, useRef, useMemo.
Для создания компонента используется функция component,
которая превращает обычную функцию в кастомный элемент:
import { html, component, useState } from 'haunted';
function Counter({ 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(Counter));
Ключевые моменты:
html — теговая функция шаблонных литералов для
рендеринга HTML.useState — хук состояния, аналог React useState.@click — синтаксис Haunted для привязки событий
(делегирует события нативным элементам).Haunted поддерживает основные хуки, позволяющие управлять состоянием и жизненным циклом компонента:
const [value, setValue] = useState('текст');
componentDidMount, componentDidUpdate и
componentWillUnmount:useEffect(() => {
console.log('Компонент отрендерился');
return () => {
console.log('Компонент размонтирован');
};
}, [value]); // зависимость
const inputRef = useRef();
const computedValue = useMemo(() => expensiveCalculation(value), [value]);
Эти хуки позволяют управлять состоянием и жизненным циклом компонента без классов, сохраняя функциональный стиль.
Haunted использует props для передачи данных компонентам. Свойства можно задать при создании элемента в DOM:
<my-counter initial="10"></my-counter>
В функции компонента свойства доступны как объект:
function Counter({ initial }) {
const [count, setCount] = useState(Number(initial) || 0);
}
Важно учитывать, что все значения из атрибутов приходят как строки, поэтому требуется явное преобразование типов.
События можно привязывать прямо через шаблон:
html`<button @click=${handleClick}>Нажми меня</button>`
При этом функция handleClick будет вызвана с объектом
события Event. Такой подход позволяет избегать ручного
поиска элементов через querySelector.
Haunted поддерживает Shadow DOM, что позволяет инкапсулировать стили:
function StyledButton() {
return html`
<style>
button {
background: #007acc;
color: white;
border: none;
padding: 10px 20px;
cursor: pointer;
}
button:hover {
background: #005fa3;
}
</style>
<button>Кнопка</button>
`;
}
customElements.define('styled-button', component(StyledButton));
При использовании Shadow DOM стили компонента не влияют на глобальные стили страницы и наоборот.
Haunted идеально подходит для разработки расширений браузера, так как позволяет создавать легковесные и изолированные UI-компоненты:
Пример использования Haunted в popup расширения Chrome:
import { html, component, useState } from 'haunted';
function Popup() {
const [enabled, setEnabled] = useState(false);
return html`
<div>
<h2>Расширение включено: ${enabled}</h2>
<button @click=${() => setEnabled(!enabled)}>
${enabled ? 'Выключить' : 'Включить'}
</button>
</div>
`;
}
customElements.define('extension-popup', component(Popup));
Этот компонент полностью автономен и может быть встроен в popup.html или content script.
Для расширений часто требуется хранение состояния между сессиями. Haunted позволяет комбинировать хуки с localStorage или API браузера:
function Toggle() {
const [enabled, setEnabled] = useState(() => {
return JSON.parse(localStorage.getItem('enabled')) || false;
});
useEffect(() => {
localStorage.setItem('enabled', JSON.stringify(enabled));
}, [enabled]);
return html`
<button @click=${() => setEnabled(!enabled)}>
${enabled ? 'Включено' : 'Выключено'}
</button>
`;
}
customElements.define('toggle-button', component(Toggle));
Использование useEffect позволяет автоматически
синхронизировать состояние с хранилищем при каждом изменении.
Haunted минималистична:
Для расширений это критично, так как UI должен быть быстрым и отзывчивым, а нагрузка на браузер минимальной.
useState,
useEffect).useRef.Haunted позволяет создавать современные, масштабируемые UI для браузерных расширений, сочетая функциональный стиль React и нативные возможности Web Components.