Структура документации

Haunted — это библиотека для создания реактивных веб-компонентов на основе стандарта Web Components и функционального подхода. Она сочетает в себе удобство работы с хуками, аналогичными React, с легковесностью нативных компонентов. Основной принцип Haunted заключается в том, что компонент — это функция, которая возвращает шаблон HTML, управляемый реактивными хуками состояния и эффектов.

Создание компонента

Компонент в Haunted определяется функцией и регистрируется через customElements.define. Простейший пример:

import { html, component } from 'haunted';

function MyComponent() {
  return html`<p>Привет, Haunted!</p>`;
}

customElements.define('my-component', component(MyComponent));

Ключевое здесь:

  • html — функция шаблонизатора для создания шаблонов с реактивными привязками.
  • component — обёртка, превращающая обычную функцию в Web Component.

Хуки состояния

Haunted использует хуки состояния, аналогичные React. Основной из них — useState. Он позволяет хранить локальное состояние компонента.

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));

Особенности useState:

  • Состояние реактивно. При изменении setCount шаблон автоматически перерисовывается.
  • Можно использовать несколько состояний в одном компоненте.
  • Хуки должны вызываться в одном и том же порядке при каждом рендере.

Эффекты и жизненный цикл

Haunted предоставляет useEffect для управления побочными эффектами, аналогично React. Он вызывается после рендера компонента.

import { html, component, useEffect, useState } 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>`;
}

customElements.define('my-timer', component(Timer));

Ключевые моменты useEffect:

  • Возвращаемая функция используется для очистки эффектов.
  • Второй аргумент — массив зависимостей. Эффект выполняется заново, если меняются зависимости.
  • Позволяет интегрировать асинхронные операции, подписки, таймеры и сторонние библиотеки.

Передача свойств

Свойства (props) передаются как атрибуты в HTML. В Haunted они доступны как аргументы функции компонента:

function Greeting({ name }) {
  return html`<p>Привет, ${name}!</p>`;
}

customElements.define('my-greeting', component(Greeting));

Особенности:

  • Свойства реактивны. Изменение атрибута обновляет компонент.
  • Можно задавать значения по умолчанию через деструктуризацию или параметры функции.
  • Для сложных типов лучше использовать объекты или JSON, передаваемый через свойства DOM.

События и взаимодействие

Haunted поддерживает нативные DOM-события. Привязка выполняется через @имя_события:

function Clicker() {
  const [count, setCount] = useState(0);

  return html`
    <button @click=${() => setCount(count + 1)}>
      Кликнули ${count} раз
    </button>
  `;
}

customElements.define('my-clicker', component(Clicker));

Дополнительно можно использовать CustomEvent для передачи событий родителю:

function Child() {
  const handleClick = () => {
    const event = new CustomEvent('child-click', {
      detail: { message: 'Нажато!' },
      bubbles: true,
      composed: true
    });
    this.dispatchEvent(event);
  };

  return html`<button @click=${handleClick}>Нажми меня</button>`;
}

Стилизация компонентов

Haunted не навязывает систему стилей. Компоненты могут использовать:

  • Встроенные стили через <style> внутри шаблона.
  • Shadow DOM для изоляции стилей.
  • Внешние CSS-файлы.

Пример с Shadow DOM:

function StyledBox() {
  return html`
    <style>
      div { color: white; background: blue; padding: 10px; }
    </style>
    <div>Стили применены!</div>
  `;
}
customElements.define('styled-box', component(StyledBox));

Работа с массивами и списками

Haunted поддерживает реактивное отображение списков через map и динамическую генерацию элементов:

function TodoList({ todos }) {
  return html`
    <ul>
      ${todos.map(todo => html`<li>${todo}</li>`)}
    </ul>
  `;
}
customElements.define('todo-list', component(TodoList));

Важно:

  • Каждый элемент должен иметь уникальный ключ, если возможны изменения порядка.
  • Изменения массива автоматически обновляют DOM при корректном использовании хуков состояния.

Расширенные хуки

Haunted поддерживает дополнительные хуки:

  • useReducer — для сложной логики состояния.
  • useRef — для доступа к DOM-элементам.
  • useMemo и useCallback — для оптимизации вычислений и функций.
import { useReducer } from 'haunted';

function reducer(state, action) {
  switch (action.type) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    default: return state;
  }
}

function Counter() {
  const [count, dispatch] = useReducer(reducer, 0);

  return html`
    <button @click=${() => dispatch({ type: 'decrement' })}>-</button>
    <span>${count}</span>
    <button @click=${() => dispatch({ type: 'increment' })}>+</button>
  `;
}

customElements.define('reducer-counter', component(Counter));

Интеграция с другими библиотеками

Haunted легко интегрируется с внешними библиотеками:

  • Можно использовать любую библиотеку для состояния, а Haunted просто рендерит результат.
  • Поддержка асинхронных операций и промисов через эффекты.

Haunted сочетает функциональный подход и нативные Web Components, обеспечивая реактивность без лишней сложности. Гибкая система хуков, декларативное управление состоянием и событиями делает её подходящей для современных веб-приложений, сохраняя лёгкость и минимальный размер кода.