Universal Router — это минималистичная, гибкая
библиотека для маршрутизации в JavaScript-приложениях, поддерживающая
как браузерные, так и серверные среды. Основной принцип работы
заключается в сопоставлении URL с набором маршрутов и вызове
соответствующих обработчиков. Маршруты представляют собой массив
объектов с ключами path и action.
import UniversalRouter from 'universal-router';
const routes = [
{ path: '/', action: () => 'Главная страница' },
{ path: '/about', action: () => 'О проекте' },
{ path: '/user/:id', action: ({ params }) => `Пользователь ${params.id}` }
];
const router = new UniversalRouter(routes);
router.resolve('/user/42').then(console.log); // Пользователь 42
Ключевой особенностью является возможность использования динамических сегментов, регулярных выражений, вложенных маршрутов и асинхронных действий.
Профилирование маршрутизации включает в себя измерение времени поиска маршрута и выполнения действий, что критично для больших приложений с сотнями маршрутов. Universal Router предоставляет гибкую архитектуру для внедрения собственного профилирования.
Метод resolve() возвращает промис, что позволяет легко
измерять время его выполнения:
const startTime = performance.now();
router.resolve('/user/42')
.then(result => {
const endTime = performance.now();
console.log(`Результат: ${result}`);
console.log(`Время маршрутизации: ${endTime - startTime} ms`);
});
Использование performance.now() вместо
Date.now() обеспечивает более точное измерение с
миллисекундной и микросекундной точностью.
Для детального анализа маршрутизации полезно вести
логирование прохода по маршрутам. Universal Router
поддерживает добавление middleware-подобных функций через обёртку
resolve:
const profiledRouter = new UniversalRouter(routes, {
resolve: (context, next) => {
const start = performance.now();
return next().then(result => {
const duration = performance.now() - start;
console.log(`[PROFILE] Маршрут ${context.path} обработан за ${duration.toFixed(2)} ms`);
return result;
});
}
});
Такая схема позволяет видеть, какой маршрут был обработан быстрее, а какой — медленнее, и выявлять узкие места.
Сложные маршруты с множеством динамических сегментов могут замедлять процесс сопоставления. Оптимальная стратегия:
/admin, /user, /shop) для
уменьшения числа проверок на верхнем уровне.const routes = [
{ path: '/', action: () => 'Главная' },
{ path: '/about', action: () => 'О проекте' },
{
path: '/user',
children: [
{ path: '/:id', action: ({ params }) => `Пользователь ${params.id}` },
{ path: '/:id/settings', action: ({ params }) => `Настройки ${params.id}` }
]
}
];
Если маршруты выполняют дорогостоящие асинхронные операции, стоит кешировать результаты для повторных запросов:
const cache = new Map();
const routes = [
{
path: '/data/:id',
action: async ({ params }) => {
if (cache.has(params.id)) return cache.get(params.id);
const data = await fetchData(params.id);
cache.set(params.id, data);
return data;
}
}
];
Это сокращает время маршрутизации и снижает нагрузку на серверные API.
Universal Router не имеет встроенного визуального профайлера, но можно интегрировать сторонние инструменты:
router.resolve.const metrics = [];
const routerWithMetrics = new UniversalRouter(routes, {
resolve: (context, next) => {
const start = performance.now();
return next().then(result => {
metrics.push({ path: context.path, duration: performance.now() - start });
return result;
});
}
});
С помощью этих данных можно строить графики времени обработки маршрутов, выявлять горячие точки и оптимизировать структуру маршрутов.
Асинхронные действия (async action) могут значительно
влиять на производительность. В таких случаях важно:
const routes = [
{
path: '/dashboard',
async action() {
const start = performance.now();
const [users, stats] = await Promise.all([fetchUsers(), fetchStats()]);
console.log(`Загрузка данных заняла ${performance.now() - start} ms`);
return { users, stats };
}
}
];
Разделение этих этапов позволяет более точно профилировать именно маршрутизацию, а не время выполнения бизнес-логики.
Universal Router одинаково хорошо работает на Node.js и в браузере. При серверном рендеринге профилирование маршрутов помогает оптимизировать SSR-время ответа, а на клиенте — UX-перформанс и переходы между страницами. Для обоих случаев полезно использовать:
resolve().