Haunted — это небольшая библиотека для создания веб-компонентов на базе Web Components, которая использует реактивный подход, вдохновлённый React Hooks. Она предоставляет легковесный способ работы с состоянием и жизненным циклом компонентов без необходимости подключать большой фреймворк.
Для работы с Haunted достаточно установить пакет через npm:
npm install haunted
Или подключить через CDN:
<script type="module">
import { html, component, useState, useEffect } from 'https://unpkg.com/haunted/haunted.js?module';
</script>
Основные экспортируемые сущности:
html — функция для создания шаблонов, аналог JSX.component — функция для регистрации
веб-компонента.useState — хук для управления локальным
состоянием.useEffect — хук для побочных эффектов.useRef, useContext и другие хуки для
расширенной логики.Компонент Haunted создаётся через функцию component,
которая принимает функцию-рендерер. Пример простого компонента с
состоянием:
import { html, component, useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<div>
<p>Счётчик: ${count}</p>
<button @click=${() => setCount(count + 1)}>Увеличить</button>
</div>
`;
}
customElements.define('my-counter', component(Counter));
Особенности:
count автоматически вызывает повторный рендер
при изменении.@событие=${handler}.useStateПозволяет хранить локальное состояние компонента:
const [value, setValue] = useState(initialValue);
value — текущее значение.setValue — функция обновления значения. Любое
обновление вызывает перерендер компонента.useReducerАльтернатива useState, подходит для сложной логики:
const [state, dispatch] = useReducer(reducer, initialState);
function reducer(state, action) {
switch(action.type) {
case 'increment': return state + 1;
default: return state;
}
}
useEffectИспользуется для выполнения побочных эффектов:
useEffect(() => {
console.log('Компонент обновлён или смонтирован');
return () => console.log('Компонент размонтирован');
}, [count]); // массив зависимостей
Haunted использует lit-html под капотом для рендеринга. Это обеспечивает:
html`
<ul>
${items.map(item => html`<li>${item}</li>`)}
</ul>
`
@event=${handler}.<div class=${condition ? 'active' : ''}></div>.useRefПозволяет получить прямой доступ к DOM-элементу:
const inputRef = useRef();
html`<input type="text" ${ref(inputRef)} />`
useContext)Обеспечивает глобальное состояние между компонентами:
const ThemeContext = createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return html`<button class=${theme}>Тема кнопки</button>`;
}
Haunted позволяет сочетать хуки для сложной логики состояния, побочных эффектов и взаимодействия с DOM.
Haunted не навязывает способ работы со стилями:
html`
<style>
p { color: red; }
</style>
<p>Текст с красным цветом</p>
`
lit-html.import { html, component, useState, useEffect } from 'haunted';
function TodoList() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
useEffect(() => {
const saved = JSON.parse(localStorage.getItem('todos') || '[]');
setTodos(saved);
}, []);
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);
const addTodo = () => {
if(input.trim() === '') return;
setTodos([...todos, input]);
setInput('');
};
return html`
<input .value=${input} @input=${e => setInput(e.target.value)} />
<button @click=${addTodo}>Добавить</button>
<ul>
${todos.map(todo => html`<li>${todo}</li>`)}
</ul>
`;
}
customElements.define('todo-list', component(TodoList));
Ключевые моменты:
useEffect обеспечивает синхронизацию с
localStorage.Haunted объединяет простоту Web Components с мощью реактивного подхода, позволяя создавать компактные и легко поддерживаемые интерфейсы. Сильная сторона — минимализм, совместимость с любыми стилями и гибкая работа с хуками.