Обработка ошибок маршрутизации

В Riot.js маршрутизация осуществляется с помощью встроенного роутера, который связывает URL-приложения с компонентами. Основная задача роутера — корректно отобразить нужный компонент при изменении адреса, однако в реальных приложениях невозможно исключить ситуации, когда путь не существует или возникает ошибка при загрузке компонента. Управление такими ошибками критически важно для поддержания стабильной работы интерфейса.


Основы маршрутизации

Riot.js использует простой, но гибкий роутер, который позволяет задавать маршруты с привязкой к компонентам:

import { component, route } from 'riot';

route('/home', 'home-component');
route('/profile/:id', 'profile-component');
route('*', 'notfound-component'); // wildcard для несуществующих маршрутов

Каждый маршрут привязывается к имени компонента. Wildcard * используется для обработки случаев, когда путь не совпал ни с одним из определённых маршрутов. Это первый шаг к обработке ошибок: любое неизвестное направление можно перенаправить на компонент с уведомлением о недоступности страницы.


Перехват ошибок при навигации

Riot.js роутер позволяет определить глобальный обработчик ошибок через событие onRouteError. Этот метод вызывается, если загрузка компонента завершилась неудачей:

route.on('routeError', (error, path) => {
  console.error(`Ошибка маршрутизации для пути ${path}:`, error);
  route('/error'); // перенаправление на страницу с ошибкой
});

Ключевой момент: error содержит объект ошибки, что позволяет логировать её или отправлять на сервер для анализа. path — путь, на котором произошла ошибка.


Асинхронная загрузка компонентов

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

route('/dashboard', async () => {
  try {
    const module = await import('./components/dashboard-component.js');
    return module.default;
  } catch (e) {
    console.error('Не удалось загрузить компонент Dashboard:', e);
    return import('./components/error-component.js');
  }
});

Здесь применяется try-catch, который позволяет предотвратить падение всего приложения и заменить неудачно загруженный компонент на запасной.


Использование fallback-компонентов

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

route('/settings', async () => {
  try {
    const module = await import('./components/settings-component.js');
    return module.default;
  } catch {
    return import('./components/settings-fallback.js');
  }
});

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


Логирование и мониторинг ошибок

Для поддержки стабильности приложения рекомендуется внедрять централизованное логирование ошибок маршрутизации. Пример:

route.on('routeError', (error, path) => {
  fetch('/api/log', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ path, message: error.message, stack: error.stack })
  });
  route('/error');
});

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


Совмещение с состоянием приложения

Ошибки маршрутизации часто требуют взаимодействия со состоянием приложения. Например, при несанкционированном доступе к защищённому маршруту:

route('/admin', () => {
  if (!store.user.isAdmin) {
    route('/forbidden');
    return;
  }
  return import('./components/admin-component.js');
});

Здесь проверка роли пользователя позволяет предотвратить загрузку компонента, а пользователю предоставляется понятная информация о недоступности страницы.


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

  • Использовать wildcard маршруты для обработки несуществующих адресов.
  • Асинхронные импорты всегда оборачивать в try-catch.
  • Создавать локальные fallback-компоненты для критически важных страниц.
  • Вести централизованное логирование ошибок для анализа и предотвращения повторений.
  • Согласовывать маршрутизацию с состоянием приложения, чтобы контролировать доступ к защищённым зонам.

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