Universal Router — это гибкая библиотека маршрутизации для JavaScript, которая работает как на клиенте, так и на сервере. В серверной среде (Node.js) она используется для генерации HTML-страниц на основе маршрутов, что позволяет реализовать серверный рендеринг (SSR) и улучшать SEO.
Маршруты в Universal Router описываются в виде массива объектов. Каждый объект маршрута может содержать следующие ключи:
/users/:id.Пример простого маршрута для сервера:
import UniversalRouter from 'universal-router';
const routes = [
{
path: '/',
action: () => `<h1>Главная страница</h1>`
},
{
path: '/users/:id',
action: ({ params }) => `<h1>Пользователь: ${params.id}</h1>`
}
];
const router = new UniversalRouter(routes);
На сервере часто требуется выполнять асинхронные операции, например,
получать данные из базы данных перед рендерингом страницы. Universal
Router поддерживает асинхронные функции async в ключе
action.
const routes = [
{
path: '/posts/:id',
async action({ params }) {
const post = await fetchPostFromDB(params.id);
return `<h1>${post.title}</h1><p>${post.content}</p>`;
}
}
];
Использование await в action позволяет
обрабатывать данные до генерации HTML и возвращать готовый контент
серверу.
Для серверного рендеринга важно передавать в маршруты контекст,
который содержит информацию о пользователе, настройках приложения или
исходные данные запроса. Контекст передаётся при вызове метода
resolve:
const context = { user: { id: 1, name: 'Alice' } };
router.resolve({ pathname: '/users/1', context }).then(html => {
console.log(html); // HTML с информацией о пользователе
});
Внутри маршрута объект context доступен как параметр,
что позволяет строить страницы динамически и безопасно.
Universal Router позволяет централизованно обрабатывать ошибки и
выполнять перенаправления. Для этого в action можно
выбросить исключение или вернуть объект с ключом
redirect.
Пример редиректа:
{
path: '/admin',
action: () => ({ redirect: '/login' })
}
Пример обработки ошибки:
{
path: '/posts/:id',
async action({ params }) {
const post = await fetchPostFromDB(params.id);
if (!post) throw new Error('Пост не найден');
return `<h1>${post.title}</h1>`;
}
}
На сервере можно перехватывать эти ошибки и формировать корректный HTTP-статус:
router.resolve({ pathname: '/posts/999' })
.then(html => response.send(html))
.catch(err => response.status(404).send('<h1>Страница не найдена</h1>'));
Вложенные маршруты позволяют строить сложные URL с общими шаблонами и компонентами. В серверном контексте это особенно полезно для генерации шаблонов с общей структурой:
const routes = [
{
path: '/',
action: () => '<h1>Главная</h1>',
children: [
{
path: 'about',
action: () => '<h1>О нас</h1>'
},
{
path: 'contact',
action: () => '<h1>Контакты</h1>'
}
]
}
];
Метод resolve автоматически учитывает вложенность и
возвращает HTML соответствующего дочернего маршрута.
Для интеграции SSR часто используют фреймворк express.
Universal Router позволяет обрабатывать все входящие HTTP-запросы через
один маршрутизатор:
import express from 'express';
import UniversalRouter from 'universal-router';
const app = express();
const router = new UniversalRouter(routes);
app.get('*', async (req, res) => {
try {
const html = await router.resolve({ pathname: req.path });
res.send(html);
} catch (err) {
res.status(404).send('<h1>Страница не найдена</h1>');
}
});
app.listen(3000);
В этом подходе все маршруты управляются централизованно, что упрощает поддержку и расширение серверной логики.
Для увеличения производительности серверного рендеринга можно кешировать результаты маршрутов. Universal Router позволяет использовать контекст и промежуточные слои для кеширования:
const cache = new Map();
const routes = [
{
path: '/posts/:id',
async action({ params }) {
if (cache.has(params.id)) return cache.get(params.id);
const post = await fetchPostFromDB(params.id);
const html = `<h1>${post.title}</h1>`;
cache.set(params.id, html);
return html;
}
}
];
Кеширование уменьшает нагрузку на базу данных и ускоряет генерацию HTML, что критично для серверного рендеринга с большим количеством пользователей.
Universal Router позволяет использовать одни и те же маршруты как на сервере, так и на клиенте, обеспечивая единую маршрутизацию в универсальных приложениях (isomorphic apps). Это упрощает синхронизацию данных и состояние приложения между сервером и браузером.
Universal Router предоставляет мощные средства для генерации страниц на сервере: асинхронные маршруты, вложенные структуры, контекст и обработку ошибок. Гибкость библиотеки позволяет строить как простые SSR-сайты, так и сложные универсальные приложения с общими маршрутами для клиента и сервера.