Client-side навигация

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


Основы маршрутизации

В Atomico маршрутизация строится вокруг объекта useRoute, который отслеживает текущий путь и параметры URL. Он предоставляет два основных инструмента:

  • route — реактивное значение текущего пути;
  • params — объект с параметрами маршрута, извлечёнными из URL.

Пример базовой настройки маршрута:

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

function App() {
    const { route, params } = useRoute({
        routes: [
            { path: "/", component: Home },
            { path: "/about", component: About },
            { path: "/user/:id", component: User }
        ]
    });

    return html`
        <nav>
            <a href="/" onCl ick=${navigate}>Главная</a>
            <a href="/about" onCl ick=${navigate}>О сайте</a>
        </nav>
        <main>
            ${route.component({ params })}
        </main>
    `;
}

function navigate(event) {
    event.preventDefault();
    history.pushState({}, "", event.currentTarget.href);
    window.dispatchEvent(new Event("popstate"));
}

В этом примере:

  • routes определяет список маршрутов и соответствующих компонентов.
  • Функция navigate предотвращает стандартное поведение ссылок и обновляет URL без перезагрузки страницы.
  • popstate инициирует обновление текущего маршрута.

Работа с параметрами маршрута

Маршруты с динамическими сегментами позволяют получать параметры напрямую:

function User({ params }) {
    return html`<h1>Пользователь ID: ${params.id}</h1>`;
}

При переходе на /user/42 значение params.id будет равно "42". Atomico автоматически сопоставляет сегменты пути с именованными параметрами.


Реактивное отслеживание изменений маршрута

Для обновления интерфейса при изменении URL используется реактивное свойство route.path. Например, можно условно отображать компонент на основе текущего пути:

const { route } = useRoute({ routes });

watchEffect(() => {
    console.log("Текущий путь:", route.path);
});

watchEffect отслеживает любые изменения в route и запускает реакцию автоматически, что позволяет создавать сложные динамические интерфейсы без сторонних библиотек.


Вложенные маршруты

Atomico поддерживает вложенные маршруты, что особенно полезно для построения многоуровневой навигации:

const { route } = useRoute({
    routes: [
        {
            path: "/dashboard",
            component: Dashboard,
            children: [
                { path: "stats", component: Stats },
                { path: "settings", component: Settings }
            ]
        }
    ]
});

Внутри компонента Dashboard можно использовать useRoute для обработки вложенных маршрутов:

function Dashboard() {
    const { route } = useRoute();
    return html`
        <aside>
            <a href="/dashboard/stats" onCl ick=${navigate}>Статистика</a>
            <a href="/dashboard/settings" onCl ick=${navigate}>Настройки</a>
        </aside>
        <section>
            ${route.component && route.component()}
        </section>
    `;
}

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


Ленивая загрузка компонентов

Для улучшения производительности применяют динамический импорт компонентов на основе маршрута:

const routes = [
    {
        path: "/profile",
        component: async () => {
            const module = await import("./Profile.js");
            return module.default;
        }
    }
];

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


Обработка несуществующих маршрутов

Для перехвата неизвестных путей используют fallback:

const { route } = useRoute({
    routes,
    fallback: NotFound
});

Компонент NotFound будет рендериться при попытке перехода на URL, который не соответствует ни одному маршруту.


Поддержка истории браузера

Atomico взаимодействует с History API, что обеспечивает корректную работу кнопок браузера «Назад» и «Вперед»:

window.addEventListener("popstate", () => {
    route.update(window.location.pathname);
});

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


Совмещение с состоянием приложения

Client-side навигация в Atomico легко интегрируется с глобальным или локальным состоянием:

import { useStore } from "atomico";

const store = useStore({ loggedIn: false });

function Login() {
    const { route } = useRoute();
    const login = () => {
        store.loggedIn = true;
        history.pushState({}, "", "/dashboard");
        window.dispatchEvent(new Event("popstate"));
    };
    return html`<button onCl ick=${login}>Войти</button>`;
}

Это позволяет изменять маршруты в ответ на события состояния, создавать защищённые маршруты и управлять доступом к разделам приложения.


Итоговая структура приложения

Применение client-side навигации с Atomico часто выглядит как сочетание трёх ключевых слоёв:

  1. Маршруты — определение пути и компонентов.
  2. Навигационные функции — управление переходами без перезагрузки.
  3. Реактивное обновление интерфейса — автоматическая подстановка компонентов и параметров.

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