Асинхронные хуки

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

Типы асинхронных хуков

Navigo поддерживает несколько типов хуков, которые могут быть асинхронными:

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

  2. after — вызывается после успешного перехода на маршрут. Используется для логирования, анимаций или дополнительных действий после рендеринга страницы.

  3. leave — вызывается при уходе с текущего маршрута. Часто применяется для сохранения состояния или подтверждения выхода.

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

Подключение асинхронного before-хука

Простейший пример before-хука с асинхронной проверкой авторизации:

const router = new Navigo('/', { hash: true });

router
  .on('/dashboard', async (params, query) => {
    console.log('Маршрут активирован', params, query);
  })
  .before(async (done, params, query) => {
    try {
      const user = await fetch('/api/auth/status').then(res => res.json());
      if (user.authenticated) {
        done(); // продолжаем переход
      } else {
        router.navigate('/login'); // перенаправление
      }
    } catch (error) {
      console.error('Ошибка авторизации', error);
      router.navigate('/error');
    }
  })
  .resolve();

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

  • Хук before принимает функцию с аргументами (done, params, query).

    • done — функция, вызываемая для продолжения маршрута после асинхронной операции.
  • Асинхронные операции выполняются через async/await или промисы.

  • В случае необходимости блокировки маршрута можно вызвать done(false) или перенаправление на другой маршрут.

Асинхронный after-хук

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

router
  .on('/profile/:id', async (params) => {
    const userData = await fetch(`/api/users/${params.id}`).then(res => res.json());
    renderProfile(userData);
  })
  .after(async (params) => {
    await analytics.trackPageView(`/profile/${params.id}`);
  })
  .resolve();

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

  • after-хук вызывается после успешного выполнения обработчика маршрута.
  • Можно использовать для асинхронных задач, не влияющих на блокировку перехода.
  • Параметры params и query доступны для использования внутри хука.

Использование leave-хуков для защиты данных

Асинхронный leave-хук полезен для предупреждения пользователя о несохранённых изменениях:

router
  .on('/editor', () => {
    renderEditor();
  })
  .leave(async (done) => {
    const confirmLeave = await showUnsavedChangesDialog();
    if (confirmLeave) {
      done(); // покидаем маршрут
    } else {
      done(false); // остаёмся на текущем маршруте
    }
  })
  .resolve();

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

  • leave-хук получает done как обязательный аргумент.
  • Возврат done(false) останавливает переход, позволяя пользователю принять решение.
  • Асинхронные функции позволяют интегрировать диалоги, таймеры или сетевые проверки перед уходом.

Советы по организации асинхронных хуков

  1. Минимизировать блокировку интерфейса Асинхронные операции в before должны выполняться быстро, чтобы не замедлять навигацию. Если операция долгая, предусмотреть индикатор загрузки.

  2. Обработка ошибок Любые ошибки внутри хуков должны быть перехвачены и корректно обработаны — это предотвращает неконтролируемый сбой маршрутизации.

  3. Разделение логики Не стоит помещать всю асинхронную логику внутрь одного хука. Лучше выделять отдельные функции для API-запросов, проверок и логирования.

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

  5. Комбинирование с динамическими маршрутами Асинхронные хуки отлично работают с параметрами маршрутов (/profile/:id), обеспечивая загрузку данных только для конкретного контекста.

Пример комплексного применения

const router = new Navigo('/', { hash: true });

async function checkAuth(done) {
  const user = await fetch('/api/auth/status').then(res => res.json());
  if (user.authenticated) {
    done();
  } else {
    router.navigate('/login');
  }
}

async function loadUserData(params) {
  return await fetch(`/api/users/${params.id}`).then(res => res.json());
}

router
  .on('/profile/:id', async (params) => {
    const data = await loadUserData(params);
    renderProfile(data);
  })
  .before(checkAuth)
  .after(async (params) => {
    await analytics.trackPageView(`/profile/${params.id}`);
  })
  .leave(async (done) => {
    const confirmLeave = await showUnsavedChangesDialog();
    if (confirmLeave) done();
    else done(false);
  })
  .resolve();

В этом примере продемонстрировано:

  • Асинхронная проверка авторизации перед входом на маршрут.
  • Загрузка данных пользователя при активации маршрута.
  • Логирование посещения страницы после рендеринга.
  • Контроль ухода с маршрута при наличии несохранённых изменений.

Асинхронные хуки в Navigo создают мощный инструмент для построения интерактивных SPA, позволяя гибко управлять навигацией, проверками, загрузкой данных и пользовательскими сценариями без блокировки интерфейса.