Декораторы маршрутов

Декораторы маршрутов в Universal Router представляют собой механизм, позволяющий добавлять дополнительное поведение или метаданные к маршрутам без изменения основной логики обработки URL. Они работают как функции высокого порядка, принимающие исходный маршрут и возвращающие новый маршрут с расширенной функциональностью.

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

Структура декоратора

Декоратор маршрута — это функция, принимающая объект маршрута и возвращающая модифицированный объект маршрута. Типичный шаблон выглядит так:

function withAuth(route) {
  return {
    ...route,
    action: async (context) => {
      if (!context.user) {
        return { redirect: '/login' };
      }
      if (route.action) {
        return route.action(context);
      }
    }
  };
}

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

Применение декораторов к маршрутам

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

Отдельный маршрут

const routes = [
  {
    path: '/dashboard',
    action: () => 'Dashboard',
  }
];

const decoratedRoutes = routes.map(withAuth);
const router = new UniversalRouter(decoratedRoutes);

Здесь маршрут /dashboard будет защищён проверкой авторизации. Любая попытка доступа без пользователя приведёт к редиректу.

Группы маршрутов

Для маршрутов, объединённых общими правилами, можно создать функцию декоратора, применяющуюся к массиву маршрутов:

function protectRoutes(routes) {
  return routes.map(withAuth);
}

const routes = protectRoutes([
  { path: '/settings', action: () => 'Settings' },
  { path: '/profile', action: () => 'Profile' }
]);

const router = new UniversalRouter(routes);

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

Композиция декораторов

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

function logRoute(route) {
  return {
    ...route,
    action: async (context) => {
      console.log(`Переход на ${route.path}`);
      return route.action ? route.action(context) : undefined;
    }
  };
}

const enhancedRoute = logRoute(withAuth({
  path: '/dashboard',
  action: () => 'Dashboard'
}));

В данном примере маршрут сначала проверяет авторизацию, а затем логирует переход. Порядок применения декораторов влияет на поведение маршрута.

Декораторы с асинхронными действиями

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

function fetchData(route, fetchFn) {
  return {
    ...route,
    action: async (context) => {
      const data = await fetchFn(context);
      context.data = data;
      return route.action ? route.action(context) : undefined;
    }
  };
}

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

Паттерн “декоратор фабрика”

Часто используют фабрики декораторов, чтобы передавать параметры при создании декоратора:

function withRole(requiredRole) {
  return (route) => ({
    ...route,
    action: (context) => {
      if (!context.user || context.user.role !== requiredRole) {
        return { redirect: '/unauthorized' };
      }
      return route.action ? route.action(context) : undefined;
    }
  });
}

const adminRoute = withRole('admin')({
  path: '/admin',
  action: () => 'Admin Panel'
});

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

Управление вложенными маршрутами

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

function applyDecoratorRecursively(route, decorator) {
  const newRoute = decorator(route);
  if (route.children) {
    newRoute.children = route.children.map(child => applyDecoratorRecursively(child, decorator));
  }
  return newRoute;
}

Таким образом, можно защитить весь сегмент маршрутов, не меняя структуру дерева маршрутов.

Практические сценарии использования

  1. Авторизация и роли – проверка доступа пользователей к определённым маршрутам.
  2. Логирование переходов – отслеживание истории навигации.
  3. Загрузка данных – асинхронное получение информации до рендеринга.
  4. Кэширование – сохранение результатов action для повторного использования.
  5. Международная локализация – динамическая подстановка переводов и контента.

Рекомендации по организации декораторов

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

Эффективное использование декораторов в Universal Router позволяет построить гибкую архитектуру маршрутизации, где логика контроля доступа, подготовки данных и дополнительных действий остаётся централизованной, модульной и легко расширяемой.