Принципы клиентской маршрутизации

Клиентская маршрутизация — механизм управления состоянием интерфейса и навигацией без перезагрузки страницы. В Riot.js маршрутизация тесно связана с компонентной архитектурой и позволяет создавать динамические одностраничные приложения (SPA).


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

Маршрутизация в Riot.js строится на принципе соответствия URL-пути и компонента, который должен быть отрисован. Основные элементы:

  • Маршрут (Route) — сопоставление URL и функции/компонента.
  • Навигация (Navigation) — изменение состояния приложения без перезагрузки страницы.
  • История (History API) — использование history.pushState и history.replaceState для управления URL.

Riot.js не предоставляет встроенного роутера, поэтому часто используются сторонние решения вроде riot-route. Однако базовые принципы можно реализовать самостоятельно через слежение за изменением window.location.hash или использованием History API.


Настройка маршрутов

Простейшая настройка с использованием riot-route выглядит так:

import route from 'riot-route';
import Home from './components/home.riot';
import About from './components/about.riot';

route('/', () => {
  mount('#app', Home);
});

route('/about', () => {
  mount('#app', About);
});

route.start(true);

Ключевые моменты:

  • route(path, callback) — задаёт обработчик для конкретного пути.
  • mount(selector, component) — монтирует компонент в DOM.
  • route.start() — активирует отслеживание изменений URL.

Маршруты можно параметризовать:

route('/user/:id', (id) => {
  mount('#app', User, { userId: id });
});

Здесь :id является динамической частью пути, передаваемой в компонент через пропсы.


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

Riot.js поддерживает передачу данных через:

  1. Динамические сегменты URL (:param) Используются для идентификации ресурсов, например, /posts/:postId.

  2. Query-параметры Получение через new URLSearchParams(window.location.search):

route('/search', () => {
  const params = new URLSearchParams(window.location.search);
  const query = params.get('q');
  mount('#app', Search, { query });
});

Управление историей навигации

История браузера играет ключевую роль:

  • route('/') — установка нового состояния без перезагрузки.
  • route('/path', true) — позволяет сразу заменить текущий URL.
  • Использование history.back() и history.forward() сохраняет корректное поведение кнопок браузера.

Важно учитывать, что при использовании хеша (#) поведение History API упрощается:

route.base('#');
route('/home', () => mount('#app', Home));

Хеш-маршрутизация не требует настройки серверной части и широко применяется в SPA.


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

Для оптимизации загрузки крупных приложений компоненты можно подключать динамически:

route('/dashboard', async () => {
  const { default: Dashboard } = await import('./components/dashboard.riot');
  mount('#app', Dashboard);
});

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


Состояние и маршруты

Компоненты, связанные с маршрутами, должны корректно обрабатывать состояние при смене URL. Основные принципы:

  • Отмена подписок и таймеров при размонтировании компонента.
  • Сброс локального состояния при переходе на другой маршрут.
  • Сохранение критически важных данных в store или глобальном состоянии.
// пример хранилища
import { observable } from 'riot-observable';

const store = observable({
  user: null
});

store.on('login', (userData) => {
  store.user = userData;
});

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


Анимации и переходы между маршрутами

Для плавного UX важно использовать анимации при смене компонентов:

mount('#app', CurrentComponent, {}, () => {
  const old = document.querySelector('#app > *:first-child');
  old?.classList.add('fade-out');
});

Riot.js позволяет легко интегрировать CSS-анимации и контролировать жизненный цикл компонентов.


Отдельные кейсы маршрутизации

  1. 404 — обработка несуществующих маршрутов:
route('*', () => {
  mount('#app', NotFound);
});
  1. Редиректы:
route('/old-path', () => route('/new-path'));
  1. Защищённые маршруты (проверка авторизации):
route('/profile', () => {
  if (!store.user) {
    route('/login');
  } else {
    mount('#app', Profile);
  }
});

Итоговые принципы

  • Маршрутизация должна быть реактивной и синхронизированной с состоянием приложения.
  • URL должен отражать текущее состояние, позволяя делиться ссылками.
  • Компоненты маршрутов управляют своим жизненным циклом, очищая ресурсы при размонтировании.
  • Для масштабируемых приложений оптимально использовать ленивую загрузку и централизованное хранилище состояния.

Эти принципы позволяют строить мощные SPA на Riot.js с гибкой и безопасной навигацией.