Динамическая регистрация

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

Принципы динамической регистрации

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

Ключевые моменты динамической регистрации:

  1. Отложенная регистрация — компонент создаётся только тогда, когда он действительно требуется.
  2. Гибкость в именах тегов — имена компонентов могут формироваться динамически.
  3. Избежание повторной регистрации — Haunted предоставляет возможность проверять, зарегистрирован ли уже элемент.

Проверка существующей регистрации

Перед динамической регистрацией важно убедиться, что компонент с таким тегом ещё не зарегистрирован, чтобы избежать ошибок DOMException: Registration failed.

function defineComponent(tagName, component) {
  if (!customElements.get(tagName)) {
    customElements.define(tagName, component);
  }
}

Функция customElements.get(tagName) возвращает конструктор зарегистрированного элемента или undefined, если элемент ещё не зарегистрирован.

Динамическое создание компонентов

Динамическая регистрация часто применяется вместе с ленивой загрузкой:

async function loadComponent(tagName, path) {
  if (!customElements.get(tagName)) {
    const module = await import(path);
    customElements.define(tagName, module.default);
  }
}

В этом примере компонент загружается только при первом обращении, а module.default — это функциональный компонент Haunted, экспортированный из модуля.

Пример функционального компонента с динамической регистрацией

import { html, component } from 'haunted';

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

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

defineComponent('my-counter', component(Counter));

При динамической регистрации этот же компонент можно подключать через функцию loadComponent, что позволяет отложить загрузку:

await loadComponent('my-counter', './counter.js');
document.body.innerHTML = `<my-counter></my-counter>`;

Интеграция с маршрутизаторами и ленивыми модулями

В SPA-приложениях динамическая регистрация позволяет загружать компоненты по маршруту:

const routes = {
  '/dashboard': () => loadComponent('dashboard-view', './views/dashboard.js'),
  '/settings': () => loadComponent('settings-view', './views/settings.js'),
};

window.addEventListener('popstate', async () => {
  const route = routes[window.location.pathname];
  if (route) await route();
});

Такой подход сокращает время первичной загрузки и поддерживает модульность проекта.

Обработка параметров при динамической регистрации

Haunted компоненты часто используют хуки для состояния (useState, useEffect). Динамическая регистрация не влияет на работу этих хуков, но важно правильно передавать параметры:

await loadComponent('user-card', './components/user-card.js');
const card = document.createElement('user-card');
card.setAttribute('user-id', '42');
document.body.appendChild(card);

Внутри компонента user-card можно использовать useEffect для загрузки данных по атрибуту user-id.

Паттерны оптимизации

  1. Кэширование модулей — повторные вызовы import() возвращают уже загруженный модуль, что снижает нагрузку.
  2. Обёртка регистрации — создание вспомогательной функции defineOnce, которая предотвращает дублирование регистрации.
  3. Асинхронные хуки — при динамической загрузке компонентов рекомендуется использовать useEffect с асинхронными вызовами для инициализации данных.

Особенности совместимости

  • Динамическая регистрация полностью поддерживается современными браузерами с ES Modules.
  • Для старых браузеров требуется полифил custom-elements-es5-adapter.
  • Ghost-рендеринг компонентов (component) от Haunted остаётся неизменным при динамической регистрации.

Резюме практических приёмов

  • Проверять регистрацию через customElements.get.
  • Использовать динамическую загрузку с import() для ленивой загрузки компонентов.
  • Передавать данные через атрибуты или свойства DOM-элемента.
  • Оборачивать регистрацию в утилиты для предотвращения повторной регистрации.
  • Интегрировать с маршрутизаторами и модульной архитектурой для SPA.

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