Shadow DOM — это ключевой механизм веб-компонентов,
обеспечивающий инкапсуляцию стилей и структуры элементов. В Stencil он
используется по умолчанию для компонентов с shadow: true,
создавая отдельное DOM-дерево, изолированное от глобальных стилей и
скриптов. Несмотря на очевидные преимущества, использование Shadow DOM
сопровождается рядом сложностей, требующих внимания при разработке.
Shadow DOM полностью изолирует стили компонента от внешнего окружения. Это приводит к следующим особенностям:
body div или .global-class не будут работать
внутри shadow root.::part и
::slotted для взаимодействия с внешними стилями.
Атрибут part позволяет открыть определённые элементы для
стилизации, а slot — управлять вставкой внешнего
контента.Пример использования ::part в Stencil:
@Component({
tag: 'my-button',
styleUrl: 'my-button.css',
shadow: true
})
export class MyButton {
render() {
return <button part="main">Нажми меня</button>;
}
}
Внешний CSS для изменения стиля кнопки:
my-button::part(main) {
background-color: red;
}
Многие сторонние UI-библиотеки и JavaScript-фреймворки ожидают стандартный DOM для работы с селекторами и событиями. При использовании Shadow DOM возникают следующие сложности:
document.querySelector, не смогут напрямую получить доступ
к внутренним элементам компонента.composed: true при создании CustomEvent:const event = new CustomEvent('customClick', { detail: 42, bubbles: true, composed: true });
this.el.dispatchEvent(event);
Без composed: true событие останется внутри shadow DOM и
будет недоступно для слушателей на родительских элементах.
Shadow DOM влияет на CSS-анимации и переходы:
@keyframes.
Ключевые кадры внутри shadow DOM локализованы, и глобальные анимации не
будут работать, если они не импортированы отдельно в CSS
компонента.Пример локальных ключевых кадров:
:host {
display: block;
animation: fadeIn 1s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
Shadow DOM требует дополнительных усилий при написании тестов:
querySelector на
document не найдут элементы внутри shadow DOM. Необходим
доступ через shadowRoot:const component = document.querySelector('my-button');
const button = component.shadowRoot.querySelector('button');
shadow() в Cypress.Использование Shadow DOM не всегда бесплатно по ресурсам:
shadow: true только для компонентов, где
требуется строгая инкапсуляция.shadow: false и управлять стилями через
обычный CSS.::part и ::slotted для
открытой стилизации элементов.composed: true для событий.Shadow DOM в Stencil предоставляет мощный механизм изоляции, но требует сознательного подхода к стилям, событиям и тестированию, особенно при интеграции с внешними библиотеками и сложными интерфейсами.