Universal Router — это гибкая библиотека для маршрутизации в JavaScript, которая поддерживает как синхронные, так и асинхронные обработчики маршрутов. Понимание различий между этими подходами и их правильное использование позволяет строить мощные и масштабируемые приложения.
Синхронный обработчик маршрута выполняется полностью сразу после сопоставления маршрута с URL. Он возвращает результат напрямую и не требует промисов или асинхронных функций.
Пример синхронного маршрута:
import UniversalRouter from 'universal-router';
const routes = [
{
path: '/home',
action: () => {
return '<h1>Главная страница</h1>';
}
},
{
path: '/about',
action: () => '<h1>О нас</h1>'
}
];
const router = new UniversalRouter(routes);
router.resolve('/home').then(result => {
console.log(result); // <h1>Главная страница</h1>
});
Ключевые особенности синхронных обработчиков:
async/await.Синхронные маршруты обеспечивают мгновенную обработку и минимальную нагрузку на Event Loop.
Асинхронные маршруты позволяют выполнять операции, которые занимают
время, например, загрузку данных через API, динамический импорт модулей
или проверку авторизации. Асинхронные функции могут возвращать промис
или использовать async/await.
Пример асинхронного маршрута с загрузкой данных:
const routes = [
{
path: '/profile/:id',
async action(context) {
const response = await fetch(`https://api.example.com/users/${context.params.id}`);
const user = await response.json();
return `<h1>Профиль пользователя: ${user.name}</h1>`;
}
}
];
const router = new UniversalRouter(routes);
router.resolve('/profile/42').then(result => {
console.log(result); // <h1>Профиль пользователя: John Doe</h1>
});
Особенности асинхронных маршрутов:
Асинхронные маршруты особенно полезны в SPA (Single Page Applications), где требуется динамическая загрузка компонентов и данных без перезагрузки страницы.
Universal Router передает объект context каждому
обработчику маршрута. Он содержит полезную информацию: параметры URL,
путь маршрута, запрос и состояние навигации. Этот объект одинаково
используется как в синхронных, так и в асинхронных маршрутах.
{
path: '/products/:category/:id',
async action({ params }) {
const { category, id } = params;
const data = await fetch(`/api/${category}/${id}`).then(res => res.json());
return `<h1>Товар: ${data.name}</h1>`;
}
}
Ключевые моменты работы с параметрами:
params автоматически извлекает значения из URL.context можно расширять дополнительными данными,
например, объектом состояния приложения.context для
передачи токенов авторизации или других сервисов.Universal Router поддерживает вложенные маршруты и цепочки действий. Асинхронные маршруты могут вызывать действия дочерних маршрутов, формируя последовательность промисов.
Пример вложенной асинхронной маршрутизации:
const routes = [
{
path: '/dashboard',
action: async () => {
const stats = await fetch('/api/stats').then(r => r.json());
return { stats };
},
children: [
{
path: '/users',
async action({ parent }) {
const users = await fetch('/api/users').then(r => r.json());
return `<h1>Пользователи</h1><pre>${JSON.stringify(users)}</pre>`;
}
}
]
}
];
Особенности:
parent.Асинхронные маршруты требуют корректного перехвата ошибок, чтобы
предотвратить сбои навигации. Universal Router позволяет использовать
конструкции try/catch внутри async action или
глобальные обработчики ошибок.
const routes = [
{
path: '/data',
async action() {
try {
const response = await fetch('/api/nonexistent');
if (!response.ok) throw new Error('Ошибка загрузки данных');
const data = await response.json();
return `<div>${data.info}</div>`;
} catch (err) {
return `<div>Произошла ошибка: ${err.message}</div>`;
}
}
}
];
Рекомендации по обработке ошибок:
try/catch внутри асинхронных
действий.В реальных приложениях часто встречаются маршруты, где часть логики выполняется синхронно, а часть требует асинхронных операций. Universal Router позволяет смешивать эти подходы в рамках одного маршрута.
const routes = [
{
path: '/blog/:id',
action: async ({ params }) => {
const title = `Пост #${params.id}`; // синхронная часть
const content = await fetch(`/api/posts/${params.id}`).then(r => r.text()); // асинхронная часть
return `<h1>${title}</h1><div>${content}</div>`;
}
}
];
Ключевые моменты:
await.Эффективное использование синхронных и асинхронных действий позволяет строить производительные маршрутизаторы с удобным управлением состоянием и данными приложения.