Shadow DOM

Shadow DOM — это технология веб-компонентов, которая позволяет инкапсулировать разметку, стили и поведение компонента, изолируя их от внешнего документа. В Haunted, библиотеке для реактивного программирования на Web Components с использованием функционального подхода, Shadow DOM играет ключевую роль в организации компонентов и управлении их состоянием.


Создание компонентов с Shadow DOM

В Haunted любой функциональный компонент можно превратить в полноценный кастомный элемент с Shadow DOM с помощью функции component:

import { component, html, render } from 'haunted';

function MyComponent({ title }) {
  return html`<h1>${title}</h1>`;
}

customElements.define('my-component', component(MyComponent, { useShadowDOM: true }));

Пояснения:

  • component оборачивает функцию в Web Component.
  • Опция useShadowDOM: true создаёт Shadow Root для компонента. Если опция не указана, Haunted по умолчанию использует Shadow DOM.
  • html используется для декларативного описания разметки компонента.

Shadow DOM создаёт локальное дерево элементов, которое изолировано от глобальных стилей и скриптов страницы. Это обеспечивает безопасное применение CSS и предотвращает конфликт имен.


Инкапсуляция стилей

Стили, добавленные внутрь Shadow Root, не влияют на элементы вне компонента:

function StyledButton() {
  return html`
    <style>
      button {
        background-color: #007bff;
        color: white;
        border: none;
        padding: 0.5em 1em;
        border-radius: 4px;
        cursor: pointer;
      }

      button:hover {
        background-color: #0056b3;
      }
    </style>
    <button>Click me</button>
  `;
}

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

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

  • CSS, определённый внутри Shadow DOM, полностью изолирован.
  • Глобальные стили страницы не воздействуют на элементы внутри компонента.
  • Можно использовать теги <style> прямо в шаблоне Haunted для локальных стилей.

Связь с внешним DOM

Несмотря на инкапсуляцию, компоненты в Haunted могут взаимодействовать с внешним миром через:

  1. Атрибуты и свойства
function Greeting({ name }) {
  return html`<p>Hello, ${name}!</p>`;
}

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

// Использование:
document.body.innerHTML = `<greeting-component name="Alice"></greeting-component>`;

Haunted автоматически синхронизирует атрибуты HTML с параметрами функции-компонента.

  1. События

Компоненты могут испускать кастомные события, которые ловятся родительским элементом:

function Counter({ count = 0 }) {
  const increment = () => {
    count++;
    dispatchEvent(new CustomEvent('count-changed', { detail: { count } }));
  };

  return html`<button @click=${increment}>Count: ${count}</button>`;
}

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

Использование Shadow DOM не препятствует подписке на события извне.


Слоты для составных компонентов

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

function Card() {
  return html`
    <style>
      .card {
        border: 1px solid #ccc;
        padding: 1em;
        border-radius: 5px;
      }
    </style>
    <div class="card">
      <slot></slot>
    </div>
  `;
}

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

// Использование:
document.body.innerHTML = `
<my-card>
  <p>Это содержимое карточки.</p>
</my-card>
`;

Принцип работы:

  • Контент из родителя вставляется в <slot>.
  • Shadow DOM сохраняет стили и структуру, но разрешает гибкую подстановку контента.

Управление Shadow DOM в Haunted

Haunted предоставляет контроль над жизненным циклом и рендерингом компонентов:

  • useEffect и useState работают в рамках Shadow DOM, что обеспечивает реактивное обновление только инкапсулированного содержимого.
  • Shadow DOM создаётся автоматически при определении компонента через component, но можно передавать дополнительные параметры:
component(MyComponent, { useShadowDOM: true, observedAttributes: ['title'] });
  • Опция observedAttributes позволяет Haunted отслеживать изменения атрибутов и автоматически перерендеривать компонент.

Особенности рендеринга и производительности

  • Shadow DOM повышает изоляцию и предсказуемость рендеринга.
  • Все обновления, вызванные состоянием (useState) или изменением атрибутов, происходят только внутри Shadow Root.
  • Это снижает вероятность конфликтов с глобальными стилями и DOM-деревом страницы.

Комбинация с глобальными стилями

Хотя Shadow DOM ограничивает влияние глобальных стилей, есть способы их интеграции:

  1. CSS Custom Properties (переменные) – могут быть определены на уровне документа и применены внутри Shadow DOM.
  2. Стили через <link> – можно подключать внешние стили прямо внутри Shadow Root.
  3. CSS-in-JS – библиотека Haunted позволяет динамически вставлять стили через шаблон html.
const color = 'red';

function DynamicStyled() {
  return html`
    <style>
      p { color: ${color}; }
    </style>
    <p>Текст с динамическим цветом</p>
  `;
}

Shadow DOM в Haunted обеспечивает строгую инкапсуляцию компонентов, повышает управляемость сложных интерфейсов и упрощает использование реактивного подхода внутри Web Components. Использование слотов, событий и атрибутов позволяет создавать гибкие и масштабируемые UI-компоненты без риска конфликтов с внешним DOM.