Haunted — это библиотека для создания веб-компонентов с использованием функционального подхода, вдохновлённого React Hooks. Она позволяет строить лёгкие, реактивные компоненты без классов, используя функции и реактивные состояния. В основе Haunted лежит концепция хуков, которые управляют состоянием, эффектами и жизненным циклом компонентов.
Ключевые хуки в Haunted:
useState(initialValue) — создаёт локальное состояние
компонента.useEffect(callback, dependencies) — выполняет побочные
эффекты при изменении зависимостей.useRef(initialValue) — создаёт ссылку на DOM-элемент
или любое значение, сохраняемое между рендерами.useContext(context) — позволяет получать значения из
контекста.useReducer(reducer, initialState) — управляет сложным
состоянием через редьюсер.Haunted использует tagged template literals для
шаблонов HTML через функцию html, что обеспечивает
декларативный рендеринг.
Компоненты в Haunted определяются функцией и регистрируются через
customElements.define. Основная структура компонента
выглядит следующим образом:
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));
Особенности:
component(Counter) превращает обычную функцию в
полноценный веб-компонент.setCount автоматически
перерисовывает компонент.useState — базовый инструмент для реактивного состояния.
Он возвращает массив [state, setState], где
state — текущее значение, а setState — функция
для его обновления.
const [text, setText] = useState('Привет');
function handleChange(event) {
setText(event.target.value);
}
При каждом вызове setText компонент перерисовывается с
новым значением. В Haunted useState полностью изолирован
внутри компонента — изменения не влияют на другие экземпляры
компонента.
Для сложного состояния, например с несколькими свойствами или
зависимостями, используется useReducer:
const initialState = { count: 0, step: 1 };
function reducer(state, action) {
switch(action.type) {
case 'increment':
return { ...state, count: state.count + state.step };
case 'setStep':
return { ...state, step: action.step };
default:
return state;
}
}
const [state, dispatch] = useReducer(reducer, initialState);
Этот подход особенно полезен, когда состояние связано и требует последовательного обновления.
useEffect управляет побочными эффектами, аналогично
методам жизненного цикла componentDidMount и
componentDidUpdate.
useEffect(() => {
console.log('Компонент смонтирован или обновился');
return () => console.log('Компонент размонтирован');
}, [count]);
useRef позволяет сохранять ссылки на DOM-элементы или
значения между рендерами:
const inputRef = useRef();
function focusInput() {
inputRef.current.focus();
}
return html`
<input ${ref(inputRef)} type="text">
<button @click=${focusInput}>Фокус</button>
`;
ref(inputRef) привязывает DOM-элемент к объекту
inputRef.Haunted поддерживает контекст для передачи данных между компонентами без проброса пропсов:
import { createContext, useContext } from 'haunted';
const ThemeContext = createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return html`<button class=${theme}>Кнопка</button>`;
}
function App() {
return html`<theme-context value="dark">
<themed-button></themed-button>
</theme-context>`;
}
Контекст упрощает управление глобальным состоянием и темами в приложении.
Haunted использует синтаксис @event для привязки
обработчиков:
<button @click=${handleClick}>Нажми</button>
Компоненты в Haunted легко комбинируются:
function Dialog({ title, content }) {
return html`
<div class="dialog">
<h2>${title}</h2>
<p>${content}</p>
</div>
`;
}
function App() {
return html`
${Dialog({ title: 'Привет', content: 'Содержимое диалога' })}
`;
}
Можно создавать собственные хуки для повторного использования состояния или эффектов:
function useCounter(initial = 0) {
const [count, setCount] = useState(initial);
const increment = () => setCount(count + 1);
return [count, increment];
}
function Counter() {
const [count, increment] = useCounter(0);
return html`<button @click=${increment}>${count}</button>`;
}
Это повышает читаемость кода и разделяет логику и шаблон.
Haunted использует виртуальный DOM на уровне шаблонов и оптимизирует обновления. Перерисовываются только изменившиеся части шаблона, что обеспечивает высокую производительность даже при сложных интерфейсах.
${} автоматически
отслеживаются.Haunted легко интегрируется с сторонними библиотеками:
lit-css или обычные
CSS-модули.component().Эта методология позволяет строить лёгкие, модульные и реактивные веб-компоненты, которые масштабируются без необходимости использования классов, сложных менеджеров состояния или дополнительных виртуальных DOM-библиотек.