Порталы — это механизм, позволяющий выводить содержимое компонента в другую часть DOM-дерева, минуя иерархию компонентов. В контексте SkateJS порталы особенно полезны для реализации модальных окон, всплывающих подсказок, контекстных меню и других элементов, которые должны визуально находиться вне родительского контейнера, сохраняя при этом реактивность данных.
В SkateJS порталы создаются с помощью функции portal(),
которая возвращает объект с методами для монтирования и обновления
содержимого. Основной принцип работы:
Пример базовой структуры:
import { Component, h, portal } from 'skatejs';
class Tooltip extends Component {
static get props() {
return {
text: String
};
}
connectedCallback() {
super.connectedCallback();
this._portal = portal(document.body);
this._portal.render(this.render());
}
disconnectedCallback() {
super.disconnectedCallback();
this._portal.remove();
}
render() {
return <div class="tooltip">{this.text}</div>;
}
}
customElements.define('my-tooltip', Tooltip);
В этом примере содержимое Tooltip выводится
непосредственно в <body>, а не внутри родительского
компонента. Это обеспечивает корректное позиционирование поверх других
элементов.
SkateJS обеспечивает реактивное обновление свойств компонента внутри портала. Если изменить свойство, связанное с отображаемым содержимым, портал автоматически обновит DOM.
this.text = 'Новый текст';
this._portal.render(this.render());
Важно всегда вызывать render() на объекте портала, чтобы
изменения отражались в целевом контейнере. В отличие от стандартного
рендера компонента, портал не автоматически реагирует на изменения, если
не использовать его метод render.
Порталы часто требуют динамического позиционирования относительно исходного элемента. Это достигается через вычисление координат родителя и обновление стилей контейнера портала:
connectedCallback() {
super.connectedCallback();
this._portal = portal(document.body);
this.updatePosition();
this._portal.render(this.render());
}
updatePosition() {
const rect = this.getBoundingClientRect();
this._portal.node.style.position = 'absolute';
this._portal.node.style.top = `${rect.bottom + window.scrollY}px`;
this._portal.node.style.left = `${rect.left + window.scrollX}px`;
}
При изменении размеров или прокрутке страницы необходимо пересчитывать позицию портала, чтобы элемент оставался на нужном месте.
При удалении компонента важно корректно очищать портал, чтобы избежать утечек памяти:
disconnectedCallback() {
super.disconnectedCallback();
if (this._portal) {
this._portal.remove();
this._portal = null;
}
}
Метод remove() полностью удаляет узел портала из DOM,
освобождая ресурсы.
SkateJS поддерживает JSX и шаблонные строки для порталов. Пример использования с JSX:
this._portal.render(
<div class="modal">
<header>Заголовок</header>
<section>{this.content}</section>
<footer>
<button oncl ick={() => this.close()}>Закрыть</button>
</footer>
</div>
);
С помощью шаблонов можно легко включать динамические данные и события, обеспечивая полную интерактивность элементов, выведенных через портал.
Модальные окна — классический пример использования порталов. Основные требования:
class ModalWindow extends Component {
static get props() {
return {
isOpen: Boolean
};
}
connectedCallback() {
super.connectedCallback();
this._portal = portal(document.body);
if (this.isOpen) {
this._portal.render(this.render());
}
}
updated() {
if (this.isOpen) {
this._portal.render(this.render());
} else {
this._portal.remove();
}
}
render() {
return <div class="modal-overlay">
<div class="modal-content">
{this.text}
</div>
</div>;
}
}
customElements.define('modal-window', ModalWindow);
Портал позволяет модальному окну не зависеть от родительских стилей, корректно накладываться поверх других элементов и оставаться полностью реактивным.
requestAnimationFrame для обновления
позиции портала при скролле для плавной анимации.this._portal = portal(targetNode).this._portal.render(this.render()).render.this._portal.remove() при
disconnectedCallback.Порталы в SkateJS позволяют строить гибкие интерфейсы с независимым визуальным слоем, обеспечивая чистоту DOM и реактивность данных, сохраняя при этом высокую производительность и контроль над визуальными эффектами.