Манипуляция узлами

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

Создание и рендеринг узлов

Для создания элементов используется функция h(tag, props, ...children), где:

  • tag — строка с именем HTML-тега или компонент Atomico;
  • props — объект с атрибутами и событиями;
  • children — дочерние узлы, строки или массивы узлов.

Пример создания узла с вложенными элементами:

import { h, render } from "atomico";

function MyComponent() {
    return h("div", { class: "container" },
        h("h1", {}, "Заголовок"),
        h("p", {}, "Описание компонента")
    );
}

render(h(MyComponent), document.body);

Ключевой момент: Atomico автоматически отслеживает изменения состояния и обновляет только те части DOM, которые изменились, минимизируя лишние операции.

Доступ к реальным DOM-узлам

Atomico предоставляет способ получения ссылки на конкретный DOM-узел через рефы. Это удобно для прямых манипуляций, например, фокусировки или измерений:

import { useRef, h } from "atomico";

function InputFocus() {
    const inputRef = useRef();

    function focusInput() {
        inputRef.current.focus();
    }

    return h("div", {},
        h("input", { ref: inputRef }),
        h("button", { onclick: focusInput }, "Фокус")
    );
}

Особенности:

  • useRef создаёт объект с полем current, который после монтирования компонента указывает на DOM-узел.
  • Изменение ref.current не вызывает повторного рендеринга компонента.

Реактивные изменения узлов

Состояние компонентов в Atomico управляется с помощью хука useState. Изменение состояния инициирует повторный рендер только тех узлов, которые зависят от этого состояния:

import { useState, h } from "atomico";

function Counter() {
    const [count, setCount] = useState(0);

    return h("div", {},
        h("span", {}, `Счетчик: ${count}`),
        h("button", { onclick: () => setCount(count + 1) }, "Увеличить")
    );
}

Вывод: любые изменения count не требуют ручной работы с DOM — Atomico обновляет текст внутри span автоматически.

Манипуляция атрибутами и классами

Атрибуты и классы задаются через объект props. Для динамических значений удобно использовать шаблонные строки или вычисляемые свойства:

function ToggleButton({ active }) {
    return h("button", {
        class: active ? "btn active" : "btn",
        onclick: () => console.log("Кнопка нажата")
    }, active ? "Включено" : "Выключено");
}
  • class и другие атрибуты обновляются при изменении состояния без полного перерендера родителя.
  • Atomico поддерживает стандартные события (onclick, oninput, onchange) и позволяет назначать несколько обработчиков через массивы.

Работа с условными и списковыми узлами

Для динамического отображения узлов применяются стандартные конструкции JavaScript. Atomico корректно рендерит массивы и пропускает null или undefined:

function ItemList({ items }) {
    return h("ul", {},
        items.map(item => h("li", {}, item)),
        items.length === 0 && h("li", {}, "Список пуст")
    );
}

Особенности:

  • Уникальные ключи (key) в списках помогают библиотеке оптимизировать повторный рендер при изменении массива.
  • Пропуск узлов через null позволяет легко управлять условным отображением.

Директивы и специальные атрибуты

Atomico поддерживает ряд специальных атрибутов, влияющих на работу с узлами:

  • ref — ссылка на узел, как уже показано выше.
  • key — уникальный идентификатор для элементов в списках.
  • slot — позволяет размещать дочерние узлы в компонентах с именованными слотами.

Пример использования слотов:

function Card({ children }) {
    return h("div", { class: "card" },
        h("header", {}, h("slot", { name: "header" })),
        h("main", {}, h("slot", {}))
    );
}

render(
    h(Card, {}, [
        h("h2", { slot: "header" }, "Заголовок карточки"),
        h("p", {}, "Содержимое карточки")
    ]),
    document.body
);

Эффекты при изменении узлов

Хук useEffect позволяет выполнять операции после монтирования или обновления узлов:

import { useEffect, h } from "atomico";

function AutoFocusInput() {
    const inputRef = useRef();

    useEffect(() => {
        inputRef.current.focus();
    }, []); // Пустой массив — эффект выполняется один раз при монтировании

    return h("input", { ref: inputRef });
}
  • Использование useEffect предотвращает необходимость в DOMContentLoaded и ручной проверке готовности узла.
  • Эффекты могут зависеть от состояния или пропсов, вызываясь только при их изменении.

Обработка событий на узлах

Atomico упрощает привязку событий. Поддерживаются как стандартные события DOM, так и пользовательские:

function InputLogger() {
    const [value, setValue] = useState("");

    return h("input", {
        value,
        oninput: (e) => setValue(e.target.value)
    });
}
  • Атрибут события автоматически передаёт объект события (Event) в обработчик.
  • Изменение состояния через событие запускает локальный рендер без перерендера родительских компонентов.

Резюме по манипуляции узлами

  • Узлы создаются через h и обновляются реактивно при изменении состояния.
  • useRef обеспечивает доступ к реальным DOM-элементам.
  • Списки и условные узлы управляются через стандартные конструкции JS с поддержкой key.
  • Атрибуты и события обновляются автоматически при изменении состояния.
  • Эффекты (useEffect) позволяют безопасно выполнять операции после рендеринга.

Манипуляция узлами в Atomico строится вокруг чистой декларативной модели, где основной задачей становится управление состоянием и структурой, а не непосредственное вмешательство в DOM, что обеспечивает эффективность и предсказуемость интерфейсов.