Обработка событий навигации

Universal Router — это гибкая библиотека маршрутизации для JavaScript, которая позволяет создавать маршруты для приложений любого типа: веб, Node.js, серверных и изоморфных приложений. Одной из ключевых возможностей является обработка событий навигации, которая позволяет контролировать переходы между маршрутами, управлять асинхронной логикой и выполнять действия до или после изменения URL.


Навигация и маршруты

Каждый маршрут в Universal Router определяется объектом с обязательным свойством path и функцией action:

const routes = [
  {
    path: '/',
    action: () => 'Главная страница'
  },
  {
    path: '/about',
    action: () => 'О сайте'
  },
  {
    path: '/user/:id',
    action: ({ params }) => `Профиль пользователя ${params.id}`
  }
];

const router = new UniversalRouter(routes);

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

  • path поддерживает динамические сегменты (:id) и регулярные выражения.
  • action вызывается при совпадении маршрута и получает объект context, содержащий параметры, путь и другие данные.
  • Переход между маршрутами осуществляется вызовом метода router.resolve(path).

Перехват навигации с помощью хуков

Universal Router позволяет обрабатывать навигацию через хуки — функции, которые выполняются до или после перехода. Основные сценарии использования:

  • проверка прав доступа перед переходом;
  • выполнение асинхронной загрузки данных;
  • перенаправление на другой маршрут.

Пример хука до перехода

const routes = [
  {
    path: '/dashboard',
    action: ({ router }) => 'Панель управления',
    before: async ({ context }) => {
      const isAuthenticated = await checkAuth();
      if (!isAuthenticated) {
        return router.resolve('/login');
      }
    }
  },
  { path: '/login', action: () => 'Страница входа' }
];

Особенности работы before:

  • Может быть синхронной или асинхронной функцией.
  • Если возвращается результат, он заменяет текущий маршрут (например, при редиректе).
  • Если возвращается undefined, переход продолжается к action.

Контекст маршрута

Контекст (context) — объект, передаваемый каждому маршруту и хукам. Важные свойства:

  • pathname — текущий путь.
  • params — параметры динамических сегментов.
  • router — ссылка на экземпляр Universal Router, полезно для вызова router.resolve внутри маршрута или хука.
  • query — объект с query-параметрами (при интеграции с URLSearchParams).

Пример использования параметров и query:

const routes = [
  {
    path: '/search/:term',
    action: ({ params, query }) => {
      return `Результаты поиска по "${params.term}", фильтр: ${query.filter}`;
    }
  }
];

router.resolve('/search/javascript?filter=recent');

Асинхронная обработка навигации

Universal Router полностью поддерживает асинхронные функции в action и before. Это позволяет выполнять:

  • загрузку данных с сервера;
  • проверку авторизации;
  • подготовку состояния приложения.

Пример асинхронного маршрута:

const routes = [
  {
    path: '/profile/:id',
    async action({ params }) {
      const user = await fetchUser(params.id);
      if (!user) return 'Пользователь не найден';
      return `Имя пользователя: ${user.name}`;
    }
  }
];

Перехват глобальных событий навигации

Для клиентских приложений на браузере часто требуется реагировать на все изменения маршрута. Это можно сделать, оборачивая вызовы router.resolve:

async function navigate(path) {
  console.log('Переход на:', path);
  const result = await router.resolve(path);
  console.log('Результат маршрута:', result);
  render(result);
}
  • Такой подход позволяет реализовать единый обработчик переходов.
  • Можно добавлять глобальные проверки, логирование, анимацию переходов и т. д.

Управление редиректами

Редирект в Universal Router осуществляется возвратом нового маршрута из action или before:

const routes = [
  {
    path: '/old-page',
    action: ({ router }) => router.resolve('/new-page')
  },
  { path: '/new-page', action: () => 'Новая страница' }
];

Важно:

  • Редирект может быть асинхронным.
  • Можно использовать router.resolve для условного редиректа, например, после проверки состояния пользователя.

Обработка ошибок навигации

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

router.resolve('/unknown').catch(error => {
  if (error.name === 'NavigationError') {
    console.log('Маршрут не найден');
  } else {
    console.error('Ошибка при навигации:', error);
  }
});

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

  • Все ошибки, возникающие в action или before, можно ловить через catch.
  • Для глобальной обработки ошибок удобно оборачивать router.resolve в функцию, которая логирует или отображает страницу ошибки.

Интеграция с History API и SPA

Для SPA важно синхронизировать навигацию с браузерной историей:

window.addEventListener('popstate', () => {
  router.resolve(window.location.pathname);
});

function navigateTo(path) {
  history.pushState(null, '', path);
  router.resolve(path);
}
  • Использование pushState позволяет изменять URL без полной перезагрузки страницы.
  • popstate обеспечивает обработку кнопок назад/вперед.

Рекомендации по структуре маршрутов

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

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