Программная навигация

Universal Router — это современная библиотека маршрутизации для JavaScript, ориентированная на работу как в браузере, так и на сервере. Она обеспечивает динамическую маршрутизацию, поддержку асинхронных действий и гибкую обработку вложенных маршрутов. Основной принцип работы Universal Router заключается в сопоставлении URL с конфигурацией маршрутов и выполнении соответствующих обработчиков.

Маршруты описываются объектами с ключами path и action. path задаёт шаблон URL, а action — функцию, которая выполняется при совпадении. Например:

import UniversalRouter from 'universal-router';

const routes = [
  {
    path: '/',
    action: () => 'Главная страница'
  },
  {
    path: '/about',
    action: () => 'О сайте'
  }
];

const router = new UniversalRouter(routes);

router.resolve('/about').then(console.log); // "О сайте"

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

Параметры маршрута

Universal Router позволяет использовать динамические сегменты в пути, обозначаемые двоеточием (:). Эти сегменты становятся доступными через объект params внутри action:

const routes = [
  {
    path: '/user/:id',
    action: ({ params }) => `Пользователь с ID: ${params.id}`
  }
];

router.resolve('/user/42').then(console.log); // "Пользователь с ID: 42"

Поддерживаются необязательные параметры (?), повторяющиеся сегменты (*) и регулярные выражения для точного контроля над допустимыми значениями.

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

Для создания вложенной навигации Universal Router использует массив children. Вложенные маршруты проверяются рекурсивно, что позволяет строить сложные структуры URL без дублирования кода.

const routes = [
  {
    path: '/dashboard',
    action: () => 'Панель',
    children: [
      {
        path: '/stats',
        action: () => 'Статистика'
      },
      {
        path: '/settings',
        action: () => 'Настройки'
      }
    ]
  }
];

В этом примере /dashboard/stats и /dashboard/settings будут корректно сопоставлены с соответствующими действиями.

Асинхронные маршруты и lazy-loading

Universal Router позволяет возвращать промисы из action, что идеально подходит для асинхронной загрузки данных или компонентов. Также поддерживается async/await синтаксис:

const routes = [
  {
    path: '/posts/:id',
    async action({ params }) {
      const response = await fetch(`/api/posts/${params.id}`);
      const post = await response.json();
      return post.title;
    }
  }
];

Асинхронные маршруты позволяют интегрировать Universal Router с API и серверными данными без дополнительного промежуточного кода.

Навигация и резолвинг маршрутов

Метод resolve() выполняет поиск подходящего маршрута и возвращает результат действия:

router.resolve('/dashboard/stats').then(result => {
  console.log(result); // "Статистика"
});

Можно передавать дополнительные контексты в resolve(), которые будут доступны во всех вложенных маршрутах:

router.resolve('/dashboard/stats', { context: { user: 'admin' } });

Обработка ошибок

Universal Router позволяет настраивать обработку ошибок маршрутизации через catch:

router.resolve('/nonexistent')
  .then(result => console.log(result))
  .catch(error => console.error('Маршрут не найден', error));

Для более гибкой обработки можно использовать специальный маршрут с path: '(.*)', который будет действовать как “404 страница”.

Хуки и middleware

Библиотека поддерживает промежуточные функции (middleware), которые выполняются перед action маршрута. Это удобно для проверки авторизации или логирования:

const routes = [
  {
    path: '/admin',
    action: () => 'Админ панель',
    before: async ({ context }) => {
      if (!context.isAdmin) throw new Error('Доступ запрещен');
    }
  }
];

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

Интеграция с фронтенд-фреймворками

Universal Router легко интегрируется с React, Vue и другими библиотеками. В React, например, можно использовать его для построения SPA без зависимости от React Router, динамически изменяя компонент:

import React, { useState, useEffect } from 'react';

function App({ router }) {
  const [page, setPage] = useState('');

  useEffect(() => {
    router.resolve(window.location.pathname).then(setPage);
  }, [window.location.pathname]);

  return <div>{page}</div>;
}

Дополнительные возможности

  • Redirects: можно возвращать объект { redirect: '/new-path' } из action для перенаправлений.
  • Custom matchers: поддержка функции match для сложной логики сопоставления URL.
  • Context propagation: любой объект контекста, переданный в resolve, доступен на всех уровнях вложенных маршрутов.

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