Интеграция с legacy-кодом

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

Использование Haunted в существующих HTML-структурах

Haunted работает через веб-компоненты, что обеспечивает совместимость с любым HTML. Для добавления 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>
  `;
}

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

После регистрации элемента <my-counter> можно вставлять его в любую часть HTML:

<div id="legacy-section">
  <my-counter initial="5"></my-counter>
</div>

Важно отметить, что Haunted-компоненты не требуют перезаписи существующего DOM. Они полностью инкапсулируют своё поведение и состояние, минимизируя конфликт с legacy-кодом.

Работа с props и атрибутами

Haunted-компоненты принимают props, которые могут быть установлены через атрибуты HTML или через JavaScript. Атрибуты автоматически преобразуются в строковые значения, поэтому для чисел или объектов рекомендуется использовать передачу через свойства:

const counterElement = document.createElement('my-counter');
counterElement.initial = 10; // свойство, а не атрибут
document.body.appendChild(counterElement);

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

Интеграция с jQuery

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

$('#legacy-section').append('<my-counter></my-counter>');

const counterElement = $('#legacy-section my-counter')[0];
counterElement.initial = 3;

Haunted не конфликтует с jQuery, так как работает с shadow DOM или инкапсулированным рендером, что предотвращает нежелательные изменения внешнего DOM.

Реактивное состояние в смешанной среде

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

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

  useEffect(() => {
    const event = new CustomEvent('countChanged', { detail: count });
    dispatchEvent(event);
  }, [count]);

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

document.addEventListener('countChanged', e => {
  console.log('Новое значение счётчика:', e.detail);
});

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

Встраивание Haunted в SPA на старом фреймворке

Если проект использует устаревшие SPA-фреймворки (например, Backbone или старую версию Angular), Haunted можно интегрировать постепенно, создавая обёртки:

function mountHaunted(selector) {
  const container = document.querySelector(selector);
  if (!container) return;

  const counter = document.createElement('my-counter');
  container.appendChild(counter);
}

mountHaunted('#app-sidebar');

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

Управление стилями

Haunted-компоненты поддерживают Shadow DOM, что обеспечивает изоляцию стилей. Для интеграции с существующим CSS можно:

  1. Использовать глобальные стили через обычные классы, если Shadow DOM не нужен (component(fn, { useShadowDOM: false })).
  2. Инкапсулировать стили внутри шаблона с помощью html и css:
const style = html`
  <style>
    p { color: blue; font-weight: bold; }
    button { padding: 5px 10px; }
  </style>
`;

return html`
  ${style}
  <p>Счётчик: ${count}</p>
  <button @click=${() => setCount(count + 1)}>+</button>
`;

Работа с событиями legacy-кода

Для интеграции с существующими событиями используют прослушку DOM-событий и передачу данных через props или события:

document.getElementById('legacy-button').addEventListener('click', () => {
  const counter = document.querySelector('my-counter');
  counter.initial += 1; // обновление состояния Haunted-компонента
});

Таким образом, Haunted может функционировать как реактивная часть старого интерфейса, не нарушая привычные процессы.

Интеграция с глобальным состоянием

В проектах с Redux, MobX или другими глобальными стор-менеджерами Haunted может подписываться на изменения:

import store from './legacy-store';

function Counter() {
  const [count, setCount] = useState(store.getState().counter);

  useEffect(() => {
    const unsubscribe = store.subscribe(() => setCount(store.getState().counter));
    return unsubscribe;
  }, []);

  return html`<p>Счётчик: ${count}</p>`;
}

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