Семантический HTML

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

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

Компонент в Haunted определяется как функция, которая возвращает шаблон с помощью html-тегов, импортируемых из haunted. Стандартная структура компонента выглядит следующим образом:

import { html, component } from 'haunted';

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

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

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

  • component(MyComponent) оборачивает функцию в веб-компонент, автоматически управляя рендерингом.
  • html используется для безопасной генерации разметки с поддержкой динамических данных.
  • Свойства компонента передаются через объект аргументов функции.

Работа с состоянием

Haunted предлагает хук useState, аналогичный React:

import { useState } from 'haunted';

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

  return html`
    <p>Счёт: ${count}</p>
    <button @click=${() => setCount(count + 1)}>Увеличить</button>
  `;
}

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

Особенности использования useState:

  • Любое обновление состояния автоматически вызывает повторный рендер компонента.
  • useState может использоваться несколько раз для управления разными переменными состояния.

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

Haunted поддерживает хук useEffect для работы с побочными эффектами:

import { useEffect } from 'haunted';

function Timer() {
  const [time, setTime] = useState(new Date().toLocaleTimeString());

  useEffect(() => {
    const interval = setInterval(() => setTime(new Date().toLocaleTimeString()), 1000);
    return () => clearInterval(interval);
  }, []);

  return html`<p>Время: ${time}</p>`;
}

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

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

  • Первый аргумент — функция, выполняемая после рендера.
  • Возвращаемая функция используется для очистки эффектов при размонтировании.
  • Второй аргумент ([]) задает зависимости; если массив пуст, эффект выполняется один раз после монтирования.

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

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

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

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

В HTML можно использовать:

<greeting-component name="Мир"></greeting-component>

Правила:

  • Свойства автоматически синхронизируются с атрибутами при изменении.
  • Атрибуты работают только с примитивными типами (строки, числа, булевы значения). Для объектов требуется ручная сериализация.

Работа с событиями

События обрабатываются с помощью синтаксиса @event:

function Clicker() {
  const [clicked, setClicked] = useState(false);

  return html`
    <button @click=${() => setClicked(!clicked)}>
      ${clicked ? 'Нажато' : 'Нажми меня'}
    </button>
  `;
}

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

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

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

Сложные шаблоны и условный рендеринг

Haunted поддерживает динамические шаблоны:

function List({ items }) {
  return html`
    <ul>
      ${items.map(item => html`<li>${item}</li>`)}
    </ul>
  `;
}

customElements.define('list-component', component(List));

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

  • Использование массивов для рендеринга списков.
  • Условные конструкции реализуются через тернарный оператор или функции, возвращающие html:
${condition ? html`<p>Да</p>` : html`<p>Нет</p>`}

Переиспользуемость и композиция

Компоненты Haunted легко комбинируются и переиспользуются. Один компонент может включать другой:

function App() {
  return html`
    <h1>Приложение</h1>
    <greeting-component name="Haunted"></greeting-component>
    <counter-component></counter-component>
  `;
}

customElements.define('app-root', component(App));

Поддержка хуков пользовательских

Можно создавать собственные хуки для инкапсуляции логики:

import { useState, useEffect } from 'haunted';

function useToggle(initial = false) {
  const [state, setState] = useState(initial);
  const toggle = () => setState(!state);
  return [state, toggle];
}

function ToggleButton() {
  const [on, toggle] = useToggle();

  return html`<button @click=${toggle}>${on ? 'Вкл' : 'Выкл'}</button>`;
}

customElements.define('toggle-button', component(ToggleButton));

Особенности пользовательских хуков:

  • Могут использовать другие хуки Haunted.
  • Обеспечивают повторное использование логики между компонентами.
  • Повышают читаемость и структурированность кода.

Интеграция с существующим DOM

Haunted можно использовать в уже существующих проектах без полной перестройки. Компоненты могут рендериться в любые элементы DOM с помощью render:

import { render, html } from 'haunted';

const root = document.getElementById('root');
render(html`<greeting-component name="Мир"></greeting-component>`, root);

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

  • Позволяет плавно внедрять реактивные компоненты в статический HTML.
  • Поддерживает реактивные обновления внутри выбранного элемента.

Оптимизация производительности

Haunted минимизирует лишние рендеры и использует только нативные возможности браузера, что снижает нагрузку на память и CPU. Для оптимизации можно:

  • Использовать мемоизацию шаблонов.
  • Разбивать большие компоненты на мелкие.
  • Контролировать обновления состояния, избегая избыточного рендеринга.

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