Shadow DOM — это технология веб-компонентов, которая позволяет инкапсулировать разметку, стили и поведение компонента, изолируя их от внешнего документа. В Haunted, библиотеке для реактивного программирования на Web Components с использованием функционального подхода, 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));
Особенности:
<style> прямо в шаблоне
Haunted для локальных стилей.Несмотря на инкапсуляцию, компоненты в Haunted могут взаимодействовать с внешним миром через:
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 с параметрами функции-компонента.
Компоненты могут испускать кастомные события, которые ловятся родительским элементом:
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>.Haunted предоставляет контроль над жизненным циклом и рендерингом компонентов:
useEffect и
useState работают в рамках Shadow DOM, что
обеспечивает реактивное обновление только инкапсулированного
содержимого.component, но можно передавать дополнительные
параметры:component(MyComponent, { useShadowDOM: true, observedAttributes: ['title'] });
observedAttributes позволяет Haunted отслеживать
изменения атрибутов и автоматически перерендеривать компонент.useState) или
изменением атрибутов, происходят только внутри Shadow Root.Хотя Shadow DOM ограничивает влияние глобальных стилей, есть способы их интеграции:
<link> – можно
подключать внешние стили прямо внутри Shadow Root.html.const color = 'red';
function DynamicStyled() {
return html`
<style>
p { color: ${color}; }
</style>
<p>Текст с динамическим цветом</p>
`;
}
Shadow DOM в Haunted обеспечивает строгую инкапсуляцию компонентов, повышает управляемость сложных интерфейсов и упрощает использование реактивного подхода внутри Web Components. Использование слотов, событий и атрибутов позволяет создавать гибкие и масштабируемые UI-компоненты без риска конфликтов с внешним DOM.