Модульные веб-компоненты
Вложение (один веб-компонент внутри другого через <slot>) — это стандартный и очень мощный способ построения интерфейсов. Он отлично работает и является основой модульного дизайна веб-компонентов.
Как элементы будут адресоваться в DOM.
1. Как будет выглядеть HTML-структура
Представим два компонента: ui-card (карточка-контейнер) и user-avatar (аватарка пользователя).
Разметка на странице (Light DOM):
<ui-card>
<h2>Профиль пользователя</h2>
<!-- Вложенный веб-компонент, который станет содержимым слота -->
<user-avatar user-id="123"></user-avatar>
</ui-card>
2. Как это работает с точки зрения Shadow DOM и слотов
Когда браузер рендерит ui-card, он берет содержимое (включая user-avatar) и перенаправляет его туда, где находится <slot> внутри Shadow DOM компонента ui-card.
Внутреннее устройство ui-card (Shadow DOM):
<!-- Это Shadow DOM компонента ui-card -->
<div class="card-wrapper">
<slot name="header"></slot> <!-- Сюда пойдет h2, если задать ему slot="header" -->
<div class="body">
<!-- А сюда пойдет все остальное содержимое, включая user-avatar -->
<slot></slot>
</div>
</div>
Визуальный результат в браузере:
В конечном счете, компонент user-avatar отображается внутри визуальной области компонента ui-card, но он сохраняет свой собственный контекст DOM и свою собственную функциональность веб-компонента.
3. Адресация и доступ через DOM
Это ключевой момент. Вложенный веб-компонент (user-avatar) сохраняет свое место в Light DOM родительской страницы, а не перемещается физически в Shadow DOM. Слот — это лишь точка вставки (placeholder).
Доступ из родительской страницы (Light DOM)
Вы обращаетесь к вложенному компоненту как обычному элементу на странице:
// Отлично работает, так как элемент находится в основном DOM
const avatar = document.querySelector('user-avatar');
avatar.setUserId(456);
Доступ из родительского компонента (ui-card)
Доступ к элементам, которые вставлены через слоты, требует использования специального API:
// В JavaScript компонента ui-card:
const slot = this.shadowRoot.querySelector('slot');
// Получаем массив всех "вставленных" элементов
const assignedElements = slot.assignedElements();
// Ищем конкретно наш вложенный компонент в этом массиве
const userAvatarComponent = assignedElements.find(el => el.tagName === 'USER-AVATAR');
if (userAvatarComponent) {
userAvatarComponent.setUserId(789);
}
Доступ из вложенного компонента (user-avatar)
Вложенный компонент не знает, что он находится внутри слота. Он просто работает как обычно:
// В JavaScript компонента user-avatar
this.addEventListener('click', () => {
console.log('Клик по аватару!');
// Он может найти своего непосредственного родителя в Light DOM (это будет <ui-card>)
console.log(this.parentElement);
});
Резюме
- Работает: Да, отлично работает.
- Инкапсуляция: Каждый компонент полностью инкапсулирован своим Shadow DOM и логикой.
- Адресация: Вложенный компонент остается в Light DOM, и к нему можно получить доступ как из основного скрипта страницы, так и из родительского компонента через API слотов (
assignedElements()).