Библиотеки для запросов

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

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

В Haunted компонент создаётся как функция. Основная функция библиотеки — component, которая превращает функцию в веб-компонент:

import { html, component } from 'haunted';

function MyCounter({ initial = 0 }) {
  const [count, setCount] = useState(initial);
  return html`
    <div>
      <p>Счётчик: ${count}</p>
      <button @click=${() => setCount(count + 1)}>Увеличить</button>
    </div>
  `;
}

customElements.define('my-counter', component(MyCounter));

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

  • html используется для шаблонов, аналогично шаблонам lit-html.
  • useState управляет локальным состоянием компонента.
  • @click привязывает обработчик события к элементу.

Реактивность через хуки

Haunted поддерживает большинство популярных хуков, аналогичных React:

  • useState(initialValue) — создаёт локальное состояние. Возвращает массив [state, setState].
  • useEffect(callback, deps) — выполняет побочные эффекты после рендера. Второй аргумент — массив зависимостей.
  • useRef(initialValue) — создаёт ссылку на DOM-элемент или произвольное значение, сохраняющееся между рендерами.
  • useReducer(reducer, initialState) — альтернатива useState, особенно полезна для сложной логики состояния.

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

import { html, component, useState, useEffect } 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 используется для очистки ресурсов при размонтировании компонента или при изменении зависимостей.

Связывание с атрибутами и свойствами

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

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

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

Атрибуты можно передавать при использовании:

<my-greeting name="Алексей"></my-greeting>

Haunted автоматически обновит компонент при изменении атрибутов.

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

Haunted поддерживает синтаксис привязки событий через @event:

function Clicker() {
  const [count, setCount] = useState(0);
  return html`<button @click=${() => setCount(count + 1)}>
    Нажато ${count} раз
  </button>`;
}

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

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

Использование сторонних библиотек

Haunted можно легко интегрировать с другими библиотеками, такими как lit-html для сложной разметки или fetch для запросов к серверу. Пример запроса данных:

import { html, component, useState, useEffect } from 'haunted';

function UserList() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(response => response.json())
      .then(data => setUsers(data));
  }, []);

  return html`
    <ul>
      ${users.map(user => html`<li>${user.name}</li>`)}
    </ul>
  `;
}

customElements.define('user-list', component(UserList));

Ключевой момент — реактивное обновление состояния setUsers автоматически пересоздаёт шаблон без дополнительного кода для рендера.

Оптимизация рендеринга

Haunted минимизирует количество обновлений DOM:

  • Изменение состояния компонента приводит к частичному перерендеру.
  • Сравнение предыдущего и текущего виртуального DOM выполняется очень быстро, без создания полноценного дерева.
  • Можно комбинировать с requestAnimationFrame или debounce для оптимизации при интенсивных изменениях.

Наследование и композиция

Компоненты Haunted легко комбинируются и наследуются. Например:

function Card({ title, content }) {
  return html`
    <div class="card">
      <h2>${title}</h2>
      <p>${content}</p>
    </div>
  `;
}

function Dashboard() {
  return html`
    <card-component title="Привет" content="Это пример карточки"></card-component>
  `;
}

customElements.define('card-component', component(Card));
customElements.define('dashboard-component', component(Dashboard));

Таким образом, создаётся иерархия компонентов, где каждый компонент остаётся независимым, а их взаимодействие строится через свойства и события.

Работа с состоянием вне компонента

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

import { useState, useEffect } from 'haunted';

const stateStore = (() => {
  let state = 0;
  const subscribers = new Set();
  return {
    get: () => state,
    set: newState => {
      state = newState;
      subscribers.forEach(fn => fn(state));
    },
    subscribe: fn => subscribers.add(fn),
    unsubscribe: fn => subscribers.delete(fn)
  };
})();

function useGlobalState() {
  const [value, setValue] = useState(stateStore.get());
  useEffect(() => {
    const UPDATE = val => setValue(val);
    stateStore.subscribe(update);
    return () => stateStore.unsubscribe(update);
  }, []);
  return [value, stateStore.se t];
}

Этот подход позволяет синхронизировать несколько компонентов без внешних библиотек управления состоянием.

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

Haunted не навязывает систему стилей. Можно использовать стандартные <style> внутри шаблона:

function StyledButton() {
  return html`
    <style>
      button {
        padding: 8px 16px;
        background: #007acc;
        color: white;
        border: none;
        border-radius: 4px;
      }
    </style>
    <button>Нажми меня</button>
  `;
}

customElements.define('styled-button', component(StyledButton));

Для глобальной стилизации можно использовать CSS Variables и Shadow DOM, чтобы компоненты оставались изолированными.

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

Haunted хорошо сочетается с fetch или любыми AJAX-библиотеками. Асинхронные запросы удобно обрабатывать в useEffect, а реактивное состояние автоматически обновляет UI. Это делает Haunted отличным инструментом для создания интерактивных приложений с динамическим контентом без тяжёлых фреймворков.