Именование маршрутов

Именование маршрутов в контексте Universal Router — это фундаментальный аспект архитектуры приложения, напрямую влияющий на читаемость кода, масштабируемость и предсказуемость поведения навигации. Грамотно выстроенная система имен позволяет избежать дублирования, упрощает поддержку и снижает вероятность логических ошибок.

Маршрут в Universal Router — это объект конфигурации, содержащий путь (path), обработчик (action) и дополнительные параметры. Имя маршрута не является обязательным свойством библиотеки, однако на практике вводится как дополнительный уровень абстракции.


Зачем нужны имена маршрутов

Использование строковых путей (/users/123) напрямую в коде приводит к ряду проблем:

  • сложность рефакторинга
  • дублирование строк
  • ошибки при ручном вводе
  • отсутствие централизованного контроля

Именованные маршруты решают эти проблемы за счёт:

  • централизации: все маршруты описываются в одном месте
  • абстракции: логика навигации не зависит от конкретных URL
  • переиспользования: один маршрут используется в разных частях приложения

Базовая структура именования

Обычно вводится объект-константа, содержащий все имена маршрутов:

export const ROUTES = {
  HOME: 'home',
  USERS: 'users',
  USER_DETAIL: 'user_detail',
  SETTINGS: 'settings',
};

Каждое значение — это уникальный идентификатор маршрута.

Далее маршруты связываются с именами:

const routes = [
  {
    name: ROUTES.HOME,
    path: '/',
    action: HomePage,
  },
  {
    name: ROUTES.USERS,
    path: '/users',
    action: UsersPage,
  },
  {
    name: ROUTES.USER_DETAIL,
    path: '/users/:id',
    action: UserDetailPage,
  },
];

Стратегии именования

1. Иерархическое именование

Используется вложенная структура для отражения логики приложения:

export const ROUTES = {
  USERS: {
    LIST: 'users.list',
    DETAIL: 'users.detail',
    EDIT: 'users.edit',
  },
};

Преимущества:

  • логическая группировка
  • удобная навигация по коду
  • предотвращение коллизий имен

2. Конвенция с разделителями

Имена строятся по шаблону:

<сущность>.<действие>

Примеры:

  • user.list
  • user.create
  • user.update
  • order.checkout

3. Использование верхнего регистра

USER_LIST
USER_DETAIL
ORDER_CHECKOUT

Подходит для небольших проектов, но хуже масштабируется.


Связь имени маршрута и URL

Именованный маршрут должен быть независим от URL:

{
  name: 'user.profile',
  path: '/profile/:id',
}

При изменении пути:

path: '/users/:id/profile'

логика приложения остаётся неизменной, если используется имя маршрута.


Генерация URL по имени

Создаётся вспомогательная функция:

function generateUrl(name, params = {}) {
  const route = routes.find(r => r.name === name);
  if (!route) throw new Error('Route not found');

  let path = route.path;

  Object.keys(params).forEach(key => {
    path = path.replace(`:${key}`, params[key]);
  });

  return path;
}

Использование:

generateUrl('user.detail', { id: 42 });
// /users/42

Обратный поиск имени маршрута

Иногда требуется определить имя маршрута по текущему URL:

function findRouteByPath(path) {
  return routes.find(route => {
    const regex = new RegExp(
      '^' + route.path.replace(/:\w+/g, '\\w+') + '$'
    );
    return regex.test(path);
  });
}

Типизация имен маршрутов (TypeScript)

Для повышения надёжности:

export const ROUTES = {
  HOME: 'home',
  USER_DETAIL: 'user.detail',
} as const;

type RouteName = typeof ROUTES[keyof typeof ROUTES];

Функция генерации URL:

function generateUrl(name: RouteName, params?: Record<string, string>): string {
  // ...
}

Динамические сегменты и именование

Маршруты с параметрами требуют согласованности:

{
  name: 'post.comment',
  path: '/posts/:postId/comments/:commentId',
}

Имена параметров должны:

  • быть осмысленными
  • соответствовать доменной модели
  • не конфликтовать друг с другом

Именование вложенных маршрутов

Universal Router поддерживает вложенные маршруты:

const routes = [
  {
    path: '/users',
    children: [
      {
        name: 'users.list',
        path: '/',
      },
      {
        name: 'users.detail',
        path: '/:id',
      },
    ],
  },
];

Важно:

  • имя отражает вложенность
  • путь может быть относительным

Namespace-подход

В больших приложениях маршруты разбиваются по модулям:

export const USER_ROUTES = {
  LIST: 'user.list',
  DETAIL: 'user.detail',
};

export const ADMIN_ROUTES = {
  DASHBOARD: 'admin.dashboard',
};

Объединение:

export const ROUTES = {
  ...USER_ROUTES,
  ...ADMIN_ROUTES,
};

Предотвращение конфликтов

Ошибка:

name: 'detail'

Правильно:

user.detail
order.detail
product.detail

Каждое имя должно быть уникальным в пределах приложения.


Консистентность именования

Нельзя смешивать стили:

Плохо:

userList
user_detail
USER_EDIT

Хорошо:

user.list
user.detail
user.edit

Интеграция с навигацией

Навигация через имена:

router.resolve({ name: 'user.detail', params: { id: 1 } });

или через обёртку:

navigate(ROUTES.USER_DETAIL, { id: 1 });

Lazy-loading и имена маршрутов

При динамической загрузке:

{
  name: 'settings.profile',
  path: '/settings/profile',
  action: () => import('./pages/Profile'),
}

Имя остаётся стабильным, независимо от способа загрузки.


Рефакторинг маршрутов

При изменении структуры:

  • имя маршрута сохраняется
  • URL может меняться
  • логика приложения не ломается

Тестирование маршрутов

Именованные маршруты упрощают тесты:

expect(generateUrl('user.detail', { id: 5 }))
  .toBe('/users/5');

Антипаттерны

1. Использование URL вместо имени

navigate('/users/1');

2. Дублирование строк

const path = '/users';
router.push('/users');

3. Неинформативные имена

route1
page2
testRoute

Рекомендации

  • использовать единый стиль именования
  • отражать доменную модель
  • избегать сокращений
  • вводить namespace
  • централизовать определения маршрутов
  • использовать типизацию при возможности

Расширение: метаданные маршрутов

Имя маршрута часто используется как ключ для метаданных:

const routeMeta = {
  'user.detail': {
    requiresAuth: true,
    title: 'User Profile',
  },
};

Связь с breadcrumbs

Имена маршрутов позволяют строить навигационные цепочки:

const breadcrumbs = {
  'user.detail': ['home', 'users.list', 'user.detail'],
};

Локализация маршрутов

Имя маршрута остаётся неизменным:

user.detail

А путь меняется:

/en/users/:id
/ru/polzovateli/:id

Масштабирование

При росте проекта:

  • добавляются новые namespace
  • увеличивается вложенность
  • возрастает роль строгой структуры имен

Именование маршрутов становится не просто соглашением, а архитектурным инструментом, обеспечивающим управляемость и предсказуемость всей системы навигации.