Universal Router — это минималистичная, гибкая библиотека маршрутизации для JavaScript, работающая как на клиентской, так и на серверной стороне. Она строится вокруг концепции маршрутных объектов и позволяет управлять переходами между страницами или состояниями приложения с помощью асинхронных обработчиков.
Маршрут представляет собой объект с ключами path,
action и необязательными children. Основное
назначение маршрута — сопоставление URL с соответствующей функцией
обработки.
const routes = [
{
path: '/home',
action: () => 'Главная страница'
},
{
path: '/about',
action: () => 'О сайте'
}
];
Для инициализации маршрутизатора используется конструктор
UniversalRouter, которому передается массив маршрутов и
опциональная конфигурация.
import UniversalRouter from 'universal-router';
const router = new UniversalRouter(routes);
Метод router.resolve(context) принимает контекст (обычно
объект с информацией о текущем URL) и возвращает результат вызова
соответствующей функции action. Этот метод
возвращает промис, что позволяет использовать
асинхронные операции, такие как запросы к API перед отображением
страницы.
router.resolve({ pathname: '/home' }).then(result => {
console.log(result); // 'Главная страница'
});
Маршруты могут быть вложенными, что позволяет организовывать сложные
иерархии страниц. Вложенные маршруты задаются с помощью свойства
children.
const routes = [
{
path: '/users',
children: [
{
path: '/:id',
action: ({ params }) => `Профиль пользователя ${params.id}`
},
{
path: '/new',
action: () => 'Создание нового пользователя'
}
]
}
];
Параметры URL определяются с помощью двоеточия (:).
Universal Router автоматически извлекает значения параметров и передает
их в объект params внутри функции action.
const routes = [
{
path: '/posts/:postId',
action: ({ params }) => `Пост с ID: ${params.postId}`
}
];
router.resolve({ pathname: '/posts/42' }).then(console.log); // 'Пост с ID: 42'
Поддерживаются также опциональные параметры и регулярные выражения:
{
path: '/products/:id(\\d+)?',
action: ({ params }) => params.id ? `Продукт ${params.id}` : 'Все продукты'
}
Функции action могут быть асинхронными, что позволяет
выполнять любые операции перед возвращением результата. Для
промежуточной обработки URL или проверки условий можно использовать
цепочку вызовов через middleware-подход.
const router = new UniversalRouter(routes, {
context: { user: { isAuthenticated: true } }
});
router.resolve({ pathname: '/admin' }, { next: true }).then(result => {
console.log(result);
});
Middleware может перехватывать переход и вызывать next()
для передачи управления следующему обработчику. Это полезно для
реализации аутентификации, логирования или динамической загрузки
данных.
const routes = [
{
path: '/admin',
action: async ({ next, context }) => {
if (!context.user.isAuthenticated) {
return 'Доступ запрещен';
}
return next();
},
children: [
{
path: '/dashboard',
action: () => 'Панель администратора'
}
]
}
];
Universal Router не навязывает конкретный способ управления историей
браузера, но часто используется совместно с History API или
библиотеками, такими как history.
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
history.listen(({ location }) => {
router.resolve({ pathname: location.pathname }).then(content => {
document.body.textContent = content;
});
});
history.push('/home');
Такой подход обеспечивает полный контроль над навигацией, позволяет реализовать SPA с асинхронной загрузкой контента и динамическим изменением состояния URL.
loadИногда нужно загружать модули динамически, не включая их в основной
бандл. Для этого используется функция load, которая
возвращает модуль с маршрутом или компонентом.
{
path: '/settings',
load: async () => import('./settings.js').then(m => m.default)
}
Universal Router позволяет обрабатывать ошибки через объект
context или через стандартные механизмы
try/catch.
router.resolve({ pathname: '/unknown' }).catch(err => {
console.error('Маршрут не найден', err);
});
Контекст передается в resolve и доступен всем маршрутам,
что позволяет делиться общими данными между обработчиками.
const context = { user: { role: 'admin' } };
router.resolve({ pathname: '/admin' }, { context });
При работе с Universal Router рекомендуется:
action для загрузки
данных перед рендерингом.load
для оптимизации SPA.context, чтобы
избежать глобальных переменных.Universal Router обеспечивает мощную и гибкую маршрутизацию, позволяя создавать как простые, так и сложные SPA и SSR-приложения без лишнего кода и зависимости от фреймворков.