Динамическое распределение

Atomico — это современная библиотека для создания веб-компонентов на JavaScript с использованием реактивного подхода. Одной из ключевых возможностей является динамическое распределение контента, позволяющее гибко управлять отображением дочерних элементов и их структурой в DOM без переписывания логики компонентов.

Shadow DOM и слоты

Atomico строится на базе стандартов Web Components, что подразумевает использование Shadow DOM. Shadow DOM инкапсулирует внутреннюю структуру компонента, изолируя стили и предотвращая конфликт с внешними элементами.

Для динамического распределения контента применяются слоты (<slot>), которые действуют как «места подстановки» для элементов, переданных внутрь компонента. Слоты бывают двух типов:

  • Именованные слоты — позволяют явно определить, какой контент попадает в конкретное место:
import { c, html } from "atomico";

function Card({ children }) {
    return html`
        <div class="card">
            <header><slot name="header"></slot></header>
            <section><slot></slot></section>
            <footer><slot name="footer"></slot></footer>
        </div>
    `;
}

Card.props = {};
customElements.define("x-card", c(Card));

В этом примере элементы с атрибутом slot="header" попадут в верхнюю часть карточки, а элементы без имени — в основной слот.

  • Неименованные слоты — используются для общего контента, который не требует специального позиционирования.

Динамическая подстановка контента

Atomico позволяет управлять содержимым слотов на лету, реагируя на изменения пропсов и состояния компонента. Для этого применяется реактивный синтаксис и функция children, возвращающая текущие дочерние элементы:

import { c, html, useState } from "atomico";

function Tabs() {
    const [active, setActive] = useState(0);

    return html`
        <div class="tabs">
            <nav>
                ${["Tab 1", "Tab 2", "Tab 3"].map((name, index) => html`
                    <button class="${index === active ? "active" : ""}" 
                            oncl ick=${() => setActive(index)}>
                        ${name}
                    </button>`)}
            </nav>
            <section>
                <slot name="tab-${active}"></slot>
            </section>
        </div>
    `;
}

customElements.define("x-tabs", c(Tabs));

Здесь контент для каждой вкладки распределяется динамически, и слот подставляется в зависимости от состояния active. Такой подход позволяет строить компоненты с изменяемой структурой DOM без повторного рендера всей иерархии.

Реактивные массивы и ключи

Для более сложного распределения полезно использовать реактивные массивы с ключами. Atomico поддерживает реактивные списки, которые автоматически обновляют DOM при изменении элементов:

import { c, html, useState } from "atomico";

function List() {
    const [items, setItems] = useState(["A", "B", "C"]);

    return html`
        <ul>
            ${items.map((item, i) => html`<li key=${i}>${item}</li>`)}
        </ul>
        <button oncl ick=${() => setItems([...items, "D"])}>Добавить</button>
    `;
}

customElements.define("x-list", c(List));

Использование атрибута key критично для корректного динамического распределения и обновления элементов, поскольку Atomico сопоставляет существующие DOM-узлы с новыми данными и минимизирует изменения в DOM.

Управление условным распределением

Динамическое распределение также включает условное отображение элементов. Atomico поддерживает стандартный JavaScript-синтаксис для условных выражений в JSX-подобных шаблонах:

import { c, html, useState } from "atomico";

function ToggleContent() {
    const [show, setShow] = useState(false);

    return html`
        <button oncl ick=${() => setShow(!show)}>Переключить</button>
        ${show && html`<div><slot></slot></div>`}
    `;
}

customElements.define("x-toggle", c(ToggleContent));

Контент в слоте появляется или исчезает без удаления всего родительского узла, что улучшает производительность при больших и сложных компонентах.

Композиция слотов и вложенные компоненты

Atomico поддерживает глубокую композицию слотов. Вложенные компоненты могут иметь свои слоты, а контент можно передавать через цепочку компонентов:

function Layout() {
    return html`
        <header><slot name="header"></slot></header>
        <main><slot></slot></main>
        <footer><slot name="footer"></slot></footer>
    `;
}

function Page() {
    return html`
        <x-layout>
            <h1 slot="header">Заголовок</h1>
            <p>Основное содержание страницы</p>
            <small slot="footer">Подвал</small>
        </x-layout>
    `;
}

При такой архитектуре динамическое распределение контента сохраняется независимо от вложенности, что делает компоненты Atomico максимально модульными.

Программное управление слотами

Для более тонкой настройки распределения контента доступна программная работа со слотами через API children и assignedNodes():

const slot = shadowRoot.querySelector("slot[name='tab-0']");
const nodes = slot.assignedNodes({ flatten: true });

Метод assignedNodes возвращает массив элементов, которые были переданы в конкретный слот, позволяя динамически модифицировать их содержимое или порядок.

Итоговые особенности динамического распределения

  • Полная изоляция DOM через Shadow DOM и слоты.
  • Реактивное управление контентом без полной перерисовки компонентов.
  • Поддержка именованных и неименованных слотов для сложной структуры.
  • Условное отображение и программное управление дочерними элементами.
  • Использование ключей для оптимизации обновлений массивов.
  • Композиция и вложенность без потери контроля над распределением контента.

Динамическое распределение в Atomico позволяет строить гибкие, производительные и модульные компоненты, где контент может изменяться на лету, сохраняя структуру и логику приложения.