Сравнение с другими подходами

Haunted — это библиотека для создания веб-компонентов на основе стандартного API Custom Elements с использованием реактивного подхода, вдохновлённого React Hooks. Она позволяет создавать динамические интерфейсы без необходимости подключать полноценный фреймворк, оставляя разработчику контроль над жизненным циклом компонентов и производительностью. Ключевое отличие Haunted — это использование функциональных компонентов и хуков вместо классовых компонентов, характерных для стандартного Web Components API.

Функциональные компоненты и хуки

Функциональный компонент в Haunted представляет собой функцию, возвращающую шаблон HTML, созданный с помощью библиотеки lit-html. Пример базового компонента:

import { html, render } from 'lit-html';
import { 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 — создаёт локальное состояние компонента.
  • component — обёртка для функции, превращающая её в полноценный веб-компонент.
  • События (@click) можно напрямую привязывать к функциям внутри компонента.

Сравнение с нативными Web Components

Web Components традиционно создаются через классы, наследуемые от HTMLElement, с методами жизненного цикла (connectedCallback, disconnectedCallback, attributeChangedCallback). Haunted упрощает этот процесс, позволяя отказаться от классов и использовать привычный функциональный стиль.

Пример нативного подхода:

class CounterElement extends HTMLElement {
  constructor() {
    super();
    this.count = 0;
    this.attachShadow({ mode: 'open' });
  }

  connectedCallback() {
    this.render();
    this.shadowRoot.querySelector('button')
      .addEventListener('click', () => {
        this.count++;
        this.render();
      });
  }

  render() {
    this.shadowRoot.innerHTML = `
      <div>
        <p>Счётчик: ${this.count}</p>
        <button>Увеличить</button>
      </div>
    `;
  }
}

customElements.define('native-counter', CounterElement);

Различия:

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

Сравнение с React

Haunted часто сравнивают с React, так как оба используют виртуальный DOM и хуки, но есть принципиальные различия:

  • Размер и зависимость: Haunted значительно легче, работает без полного React Runtime.
  • Web Components: Компоненты Haunted сразу являются нативными элементами DOM, что упрощает интеграцию с другими библиотеками и фреймворками.
  • Реактивность: React требует оборачивания компонентов в JSX и использования ReactDOM.render, тогда как Haunted рендерит шаблоны напрямую через lit-html.
  • Жизненный цикл: Haunted использует хуки (useEffect, useState и др.) в функциональных компонентах, что делает код более декларативным и приближает к React-хукам, но с меньшим накладным весом.

Сравнение с Lit и LitElement

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

Особенность Haunted Lit / LitElement
Стиль компонентов Функциональный Классовый
Реактивность Через хуки (useState) Через свойства и @property
Интеграция с DOM Нативный Web Component Нативный Web Component
Поддержка JSX Нет (только lit-html) Нет (lit-html)
Размер сборки Минимальный Средний

Главное отличие Haunted — это упрощение синтаксиса и возможность использовать привычные React-подобные паттерны без полной зависимости от React или сложного класса LitElement.

Использование хуков для сложной логики

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

  • useState — управление локальным состоянием.
  • useEffect — реагирование на изменения состояния или жизненного цикла компонента.
  • useRef — хранение ссылок на DOM-элементы без повторного рендера.
  • useReducer — для более сложного управления состоянием, аналог Redux-подхода.

Пример использования useEffect:

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 здесь полностью заменяет необходимость в connectedCallback и disconnectedCallback, делая код более компактным и декларативным.

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

Haunted легко интегрируется с любыми библиотеками, работающими с DOM: D3, Three.js, Chart.js и даже React-компонентами через wrapper. В отличие от React, нет необходимости оборачивать все в ReactDOM.createRoot. Это делает Haunted подходящим для постепенной миграции существующих проектов или микрофронтенд-структур.

Итоговое сравнение подходов

Haunted сочетает преимущества функциональных компонентов и хуков с нативными Web Components, минимизируя объем зависимостей и повышая производительность. По сравнению с классическим Web Components, упрощает работу с состоянием и событиями. В сравнении с React и Lit, предлагает легковесную альтернативу с нативной интеграцией и реактивностью, сохраняя декларативный стиль разработки и гибкость для сложной логики компонентов.