Маршруты на основе данных

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

Ключевым элементом является возможность описания маршрутов не только через строковые пути, но и через объекты с методами, которые могут динамически возвращать следующие маршруты, что обеспечивает гибкость при работе с большими приложениями, API и CMS-системами.


Определение маршрута через функцию

Маршрут может быть представлен в виде функции, возвращающей объект маршрута или массив маршрутов. Это полезно, когда структура маршрутов зависит от данных, получаемых на сервере или из внешнего API.

import UniversalRouter from 'universal-router';

const routes = async () => {
  const categories = await fetch('/api/categories').then(res => res.json());

  return categories.map(category => ({
    path: `/category/${category.slug}`,
    action: () => `Страница категории: ${category.name}`
  }));
};

const router = new UniversalRouter(await routes());

router.resolve('/category/sports').then(console.log);

Ключевые моменты:

  • Использование async функций позволяет загружать данные перед формированием маршрутов.
  • Каждый объект маршрута может содержать path и action, как в обычной маршрутизации.
  • Масштабируемость: легко добавлять новые маршруты на основе динамических данных.

Динамические параметры и вложенные маршруты

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

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

Особенности:

  • :id автоматически извлекается в объект params.
  • Вложенные маршруты могут быть реализованы через свойство children, позволяя строить древовидную структуру маршрутов.
const routes = [
  {
    path: '/dashboard',
    children: [
      { path: '', action: () => 'Главная панель' },
      { path: 'settings', action: () => 'Настройки' }
    ]
  }
];

Генерация маршрутов на основе API

Маршруты на основе данных особенно удобны при интеграции с внешними источниками, такими как CMS или REST API. В этом случае структура URL формируется динамически.

const fetchPages = async () => {
  const pages = await fetch('/api/pages').then(res => res.json());
  return pages.map(page => ({
    path: `/${page.slug}`,
    action: () => page.content
  }));
};

const router = new UniversalRouter(await fetchPages());

Преимущества подхода:

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

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

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

const routes = [
  {
    path: '/product/:id',
    action: async ({ params }) => {
      const product = await fetch(`/api/products/${params.id}`).then(res => res.json());
      if (!product.available) {
        return 'Товар недоступен';
      }
      return `Страница товара: ${product.name}`;
    }
  }
];

Ключевые моменты:

  • Асинхронная логика внутри action позволяет учитывать состояние данных (например, наличие товара).
  • Маршрут может возвращать разные результаты в зависимости от полученных данных.
  • Поддерживаются вложенные асинхронные маршруты с проверкой условий на каждом уровне.

Совмещение статических и динамических маршрутов

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

const staticRoutes = [
  { path: '/', action: () => 'Главная' },
  { path: '/about', action: () => 'О компании' }
];

const dynamicRoutes = async () => {
  const posts = await fetch('/api/posts').then(res => res.json());
  return posts.map(post => ({
    path: `/posts/${post.slug}`,
    action: () => post.title
  }));
};

const router = new UniversalRouter([
  ...staticRoutes,
  ...(await dynamicRoutes())
]);

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

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

Ленивая загрузка маршрутов и код-сплиттинг

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

const routes = [
  {
    path: '/dashboard',
    action: async () => {
      const module = await import('./DashboardPage.js');
      return module.default;
    }
  },
  {
    path: '/profile/:id',
    action: async ({ params }) => {
      const module = await import('./ProfilePage.js');
      return module.default(params.id);
    }
  }
];

Выводы по методам ленивой загрузки:

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

Советы по организации больших наборов маршрутов

  1. Декомпозиция — разделение маршрутов по модулям и функциям, например: routes/users.js, routes/posts.js.
  2. Асинхронная инициализация — динамические маршруты формируются при старте приложения или при первой необходимости.
  3. Использование children — вложенные маршруты упрощают структуру и позволяют переиспользовать общие части.
  4. Условная логика — проверка состояния данных перед отображением страницы улучшает UX и предотвращает ошибки.
  5. Кэширование — при работе с внешними API рекомендуется кэшировать маршруты для ускорения навигации.

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