Цепочки хуков

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


Регистрация хуков

В Navigo хуки добавляются через объект конфигурации при создании маршрутизатора или через методы on/before/after конкретного маршрута. Основные типы хуков:

  1. before — выполняется перед обработкой маршрута. Может отменить переход, вызвав done(false) или перенаправить пользователя.
  2. after — выполняется после успешной обработки маршрута. Обычно используется для логирования, аналитики или управления состоянием UI.
  3. already — срабатывает, если текущий маршрут совпадает с предыдущим и переход не требуется.

Пример базовой регистрации:

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

router.before((done, params, route) => {
  console.log('Перед переходом:', route.url);
  done(); // продолжение цепочки
});

router.on('/home', () => {
  console.log('Маршрут /home обработан');
});

router.after((params, route) => {
  console.log('После перехода:', route.url);
});

router.resolve();

Последовательность выполнения

Цепочка хуков строится по принципу FIFO (первым вошёл — первым выполнен). Для одного маршрута порядок работы выглядит так:

  1. Глобальные before хуки
  2. Маршрутные before хуки
  3. Обработчик маршрута (on)
  4. Маршрутные after хуки
  5. Глобальные after хуки

Важно понимать, что каждый before хук должен вызывать функцию done(), иначе переход остановится. Функция done может принимать аргументы:

  • done() — продолжить переход.
  • done(false) — отменить переход.
  • done('/new-route') — перенаправить на другой маршрут.

Пример условного перехода с отменой:

router.before((done, params, route) => {
  if (!user.isAuthenticated) {
    done('/login'); // перенаправление на страницу логина
  } else {
    done();
  }
});

Вложенные и маршрутные хуки

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

router.on('/dashboard', () => {
  console.log('Dashboard открыт');
})
.before((done, params, route) => {
  console.log('Проверка прав на Dashboard');
  done();
})
.after((params, route) => {
  console.log('Локальная аналитика Dashboard');
});

В этом примере сначала срабатывает глобальный before, затем локальный before, потом обработчик маршрута, далее локальный after, и в конце глобальный after.


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

Цепочки хуков полностью поддерживают асинхронное выполнение. Любой хук может использовать промисы или асинхронные функции, при этом переход будет ожидать вызова done():

router.before(async (done, params, route) => {
  const hasAccess = await checkUserPermissions(params.id);
  if (hasAccess) {
    done();
  } else {
    done('/forbidden');
  }
});

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


Передача параметров через цепочку

Каждый хук получает одинаковые аргументы:

  • done — функция управления переходом.
  • params — параметры маршрута, включая динамические сегменты и query-параметры.
  • route — объект маршрута с информацией о пути, хосте и состоянии.

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

router.before((done, params, route) => {
  params.token = getAuthToken();
  done();
});

router.on('/profile/:id', (params) => {
  fetchProfile(params.id, params.token)
    .then(profile => renderProfile(profile));
});

Условные цепочки

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

router.before((done, params) => {
  if (params.id === 'admin') {
    done('/access-denied'); // редирект
  } else {
    done();
  }
});

router.before((done) => {
  console.log('Общий before для всех маршрутов');
  done();
});

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


Логирование и аналитика

Использование цепочек хуков упрощает внедрение аналитики:

router.after((params, route) => {
  analytics.trackPage(route.url, params);
});

Можно подключить несколько after хуков для разных целей — один для логирования, другой для обновления UI, третий для синхронизации состояния приложения.


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

  • Минимизировать логику внутри одного хука: лучше разбивать на несколько мелких функций для читаемости и поддержки.
  • Явно вызывать done() в каждом before хукe.
  • Использовать локальные хуки для специфичных маршрутов, чтобы не загромождать глобальный контекст.
  • Асинхронные хуки оформлять с обработкой ошибок через try/catch и перенаправления при необходимости.
  • Параметры маршрута использовать как общий контекст между хуками, избегая глобальных переменных.

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