Композиция маршрутов

В Universal Router маршруты представляют собой объекты с определёнными свойствами, среди которых ключевыми являются path, action и children. Композиция маршрутов позволяет создавать иерархическую структуру маршрутов, упрощая управление сложными приложениями с большим количеством путей.

const routes = [
  {
    path: '/users',
    action: () => 'Список пользователей',
    children: [
      {
        path: '/:id',
        action: ({ params }) => `Профиль пользователя ${params.id}`
      }
    ]
  }
];

В этом примере маршрут /users/:id является дочерним для /users. Иерархия маршрутов отражает структуру URL и позволяет автоматически наследовать параметры и контекст.


Использование свойства children

Свойство children — это массив маршрутов, вложенных в родительский маршрут. Оно позволяет:

  • Разбивать большие маршруты на логические блоки.
  • Избегать дублирования кода для общих частей URL.
  • Передавать контекст и параметры родительского маршрута в дочерние.
const routes = [
  {
    path: '/dashboard',
    action: () => 'Главная панель',
    children: [
      {
        path: '/analytics',
        action: () => 'Аналитика',
      },
      {
        path: '/settings',
        action: () => 'Настройки',
      }
    ]
  }
];

При обращении к /dashboard/analytics Universal Router сначала сопоставляет /dashboard, затем ищет соответствие среди дочерних маршрутов.


Наследование и объединение параметров

При использовании вложенных маршрутов параметры и контекст родителя автоматически передаются в дочерние маршруты.

const routes = [
  {
    path: '/users/:userId',
    action: ({ params }) => `Пользователь ${params.userId}`,
    children: [
      {
        path: '/posts/:postId',
        action: ({ params }) => `Пост ${params.postId} пользователя ${params.userId}`
      }
    ]
  }
];

Запрос к /users/42/posts/100 создаёт объект params:

{
  userId: '42',
  postId: '100'
}

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


Вложенные маршруты с асинхронными действиями

Universal Router поддерживает асинхронные функции для action. При композиции маршрутов это позволяет загружать данные на разных уровнях иерархии.

const routes = [
  {
    path: '/products',
    action: async () => fetch('/api/products').then(res => res.json()),
    children: [
      {
        path: '/:id',
        action: async ({ params }) => fetch(`/api/products/${params.id}`).then(res => res.json())
      }
    ]
  }
];

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


Комбинирование маршрутов через parent и children

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

function createProductRoutes() {
  return [
    {
      path: '/:id',
      action: ({ params }) => `Продукт ${params.id}`
    },
    {
      path: '/:id/reviews',
      action: ({ params }) => `Отзывы для продукта ${params.id}`
    }
  ];
}

const routes = [
  {
    path: '/products',
    action: () => 'Список продуктов',
    children: createProductRoutes()
  }
];

Это позволяет создавать модульные маршруты, которые можно использовать повторно в разных частях приложения.


Роутинг с промежуточными уровнями

Universal Router поддерживает промежуточные уровни маршрутов, которые могут не иметь собственного action, а служить только для организации структуры.

const routes = [
  {
    path: '/app',
    children: [
      {
        path: '/users',
        action: () => 'Пользователи'
      },
      {
        path: '/settings',
        action: () => 'Настройки'
      }
    ]
  }
];

При запросе /app/users вызывается только действие дочернего маршрута. Родительский маршрут служит контейнером, обеспечивая общую структуру URL.


Условная композиция маршрутов

Маршруты могут быть добавлены в иерархию динамически в зависимости от условий:

const adminRoutes = [
  {
    path: '/dashboard',
    action: () => 'Панель администратора'
  }
];

const routes = [
  {
    path: '/',
    action: () => 'Главная',
    children: isAdmin ? adminRoutes : []
  }
];

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


Обработка ошибок на разных уровнях

Композиция маршрутов позволяет назначать отдельные обработчики ошибок на каждом уровне иерархии с помощью свойства action и блоков try/catch в асинхронных действиях:

const routes = [
  {
    path: '/users',
    action: async () => {
      try {
        return await fetchUsers();
      } catch (err) {
        return 'Ошибка загрузки пользователей';
      }
    },
    children: [
      {
        path: '/:id',
        action: async ({ params }) => {
          try {
            return await fetchUser(params.id);
          } catch (err) {
            return `Ошибка загрузки пользователя ${params.id}`;
          }
        }
      }
    ]
  }
];

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


Итоговые принципы композиции

  • Иерархия маршрутов отражает структуру URL.
  • Дочерние маршруты наследуют параметры и контекст родителя.
  • Асинхронные действия можно вызывать на любом уровне.
  • Динамическая и условная композиция упрощает управление сложными приложениями.
  • Промежуточные уровни позволяют организовывать маршруты без повторного определения action.
  • Локальная обработка ошибок делает маршрутизатор более устойчивым и гибким.

Комплексное применение этих принципов обеспечивает модульность, повторное использование и простоту поддержки маршрутов в больших JavaScript-приложениях.