Грамотно организованная структура маршрутов — ключевой элемент при работе с Universal Router. От того, как спроектированы маршруты, зависит читаемость кода, масштабируемость приложения и удобство поддержки.
В основе лежат три базовых принципа:
В Universal Router маршрут представляет собой объект, содержащий:
path)handler)children)Простейший пример:
const routes = [
{
path: '/',
handler: homeHandler
},
{
path: '/about',
handler: aboutHandler
}
];
Каждый маршрут — это изолированная единица логики, которая может быть расширена при необходимости.
Иерархия маршрутов реализуется через свойство children.
Это позволяет описывать сложные структуры URL без дублирования.
const routes = [
{
path: '/users',
handler: usersHandler,
children: [
{
path: '/:id',
handler: userProfileHandler
},
{
path: '/:id/edit',
handler: userEditHandler
}
]
}
];
В крупных приложениях маршруты нельзя хранить в одном файле. Используется модульная структура:
// routes/users.js
export const userRoutes = {
path: '/users',
children: [
{
path: '/:id',
handler: userProfileHandler
}
]
};
// routes/index.js
import { userRoutes } from './users.js';
import { productRoutes } from './products.js';
export const routes = [
userRoutes,
productRoutes
];
Dynamic segments позволяют обрабатывать переменные части URL:
{
path: '/products/:productId',
handler: productHandler
}
В обработчике параметры доступны через контекст:
async function productHandler(context) {
const { productId } = context.params;
return getProduct(productId);
}
Для логической организации используется группировка:
const adminRoutes = {
path: '/admin',
children: [
{
path: '/users',
handler: adminUsersHandler
},
{
path: '/settings',
handler: adminSettingsHandler
}
]
};
Universal Router позволяет внедрять промежуточные обработчики:
async function authMiddleware(context, next) {
if (!context.user) {
throw new Error('Unauthorized');
}
return next();
}
Применение:
{
path: '/dashboard',
action: authMiddleware,
children: [
{
path: '',
handler: dashboardHandler
}
]
}
Повторяющиеся шаблоны маршрутов выносятся в функции:
function createCrudRoutes(basePath, handlers) {
return {
path: basePath,
children: [
{ path: '', handler: handlers.list },
{ path: '/create', handler: handlers.create },
{ path: '/:id', handler: handlers.view },
{ path: '/:id/edit', handler: handlers.edit }
]
};
}
Использование:
const productRoutes = createCrudRoutes('/products', {
list: productListHandler,
create: productCreateHandler,
view: productViewHandler,
edit: productEditHandler
});
Для оптимизации загрузки используются динамические импорты:
{
path: '/reports',
async handler() {
const module = await import('./handlers/reports.js');
return module.default();
}
}
Ошибки должны обрабатываться централизованно:
const router = new UniversalRouter(routes, {
errorHandler: (error, context) => {
console.error(error);
return renderErrorPage(error);
}
});
Маршруты могут содержать дополнительные поля:
{
path: '/profile',
handler: profileHandler,
meta: {
requiresAuth: true,
title: 'User Profile'
}
}
Использование метаданных:
async function middleware(context, next) {
if (context.route.meta.requiresAuth && !context.user) {
throw new Error('Unauthorized');
}
return next();
}
Важно соблюдать единый стиль написания путей:
/path)Пример ошибки:
path: 'users' // плохо
Правильный вариант:
path: '/users'
Сложные приложения используют композицию:
const routes = [
{
path: '/',
children: [
publicRoutes,
authRoutes,
adminRoutes
]
}
];
При росте приложения применяются:
Feature-based структура
Domain-driven подход
Layered architecture
Слишком глубокая структура:
/users/:id/settings/security/password/change
Усложняет поддержку и понимание.
{ path: '/users/list' }
{ path: '/users/all' }
Лучше использовать единый маршрут.
Маршрут не должен:
Пример:
test('routes to user profile', async () => {
const result = await router.resolve('/users/123');
expect(result).toBeDefined();
});
Пример:
/routes
/auth
login.js
register.js
/users
index.js
profile.js
index.js
Каждая директория:
Контекст маршрута часто включает:
const router = new UniversalRouter(routes, {
context: {
user: currentUser,
store
}
});
Маршруты становятся частью общей архитектуры, а не изолированной системой.