Custom Elements API

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

Регистрация компонентов

В Haunted каждый компонент создаётся как функция, возвращающая шаблон разметки с помощью html из библиотеки. Для интеграции с браузером используется стандартный метод customElements.define.

Пример:

import { html, render } from 'haunted';

function MyComponent({ name }) {
  return html`<p>Hello, ${name}!</p>`;
}

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

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

  • haunted() — оборачивает функцию в Web Component, автоматически управляя состоянием и рендером.
  • Имя компонента должно содержать дефис, как того требует стандарт Custom Elements.
  • Можно использовать любые свойства и атрибуты, которые передаются в функцию через объект аргументов.

Свойства и атрибуты

Haunted автоматически связывает свойства и атрибуты элемента:

function Greeting({ message }) {
  return html`<h1>${message}</h1>`;
}

customElements.define('greeting-element', haunted(Greeting));
  • Атрибут message="Привет" на <greeting-element> будет доступен в качестве свойства message.
  • Для типов, отличных от строки, рекомендуется использовать свойства напрямую через Jav * aScript:
const elem = document.createElement('greeting-element');
elem.message = 'Добро пожаловать';
document.body.appendChild(elem);

Хуки в Haunted

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

  1. useState — управление локальным состоянием:
import { useState } from 'haunted';

function Counter() {
  const [count, setCount] = useState(0);
  return html`
    <button @click=${() => setCount(count + 1)}>
      Count: ${count}
    </button>
  `;
}
  • setCount вызывает повторный рендер компонента автоматически.
  • Состояние привязано к конкретному экземпляру Custom Element.
  1. useEffect — побочные эффекты:
import { useEffect } from 'haunted';

function Timer() {
  const [time, setTime] = useState(0);

  useEffect(() => {
    const id = setInterval(() => setTime(t => t + 1), 1000);
    return () => clearInterval(id); // очистка при размонтировании
  }, []); // пустой массив: эффект выполняется один раз
}
  • Аналогично React: можно передавать массив зависимостей для контроля вызова эффекта.
  • Эффект очищается автоматически при удалении элемента со страницы.

Слоты и Shadow DOM

Haunted полностью поддерживает Shadow DOM и слоты, что позволяет создавать изолированные компоненты с возможностью вставки пользовательского контента:

function Card() {
  return html`
    <div class="card">
      <slot name="header"></slot>
      <slot></slot>
    </div>
  `;
}

customElements.define('my-card', haunted(Card));
  • Элемент <slot> позволяет размещать контент внутри компонента.
  • Атрибут name на слоте соответствует атрибуту slot у вложенных элементов.

Обработка событий

События внутри Haunted-компонентов связываются через @event синтаксис:

function Clicker() {
  const [count, setCount] = useState(0);
  return html`
    <button @click=${() => setCount(count + 1)}>
      Нажато ${count} раз
    </button>
  `;
}
  • @click — синтаксический сахар для addEventListener('click', ...).
  • Все обработчики автоматически корректно работают с обновлением состояния и повторным рендером.

Наследование и расширение Custom Elements

Haunted позволяет расширять существующие элементы и создавать сложные компоненты через стандартные механизмы Web Components:

class FancyButton extends HTMLButtonElement {
  connectedCallback() {
    this.style.color = 'red';
  }
}

customElements.define('fancy-button', FancyButton, { extends: 'button' });
  • extends позволяет создавать кастомные элементы на основе нативных тегов.
  • Haunted совместим с такими элементами, если использовать обертку haunted().

Встроенная реактивность

Ключевая особенность Haunted — реактивность без виртуального DOM:

  • Каждый вызов setState инициирует рендер только текущего элемента.
  • Используются нативные методы Custom Elements и Shadow DOM для минимизации лишних перерисовок.
  • Обновления происходят максимально эффективно, что делает библиотеку лёгкой и быстрой.

Итоговые рекомендации по использованию

  • Использовать хуки для управления состоянием и эффектами.
  • Всегда регистрировать компоненты через customElements.define.
  • Предпочтительно использовать Shadow DOM для изоляции стилей и контента.
  • Для сложных взаимодействий компонентов применять слоты и события через @event.
  • Встроенная реактивность и простота Haunted делают её идеальной для создания небольших и масштабируемых Web Components без тяжёлого фреймворка.

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