Хук page.exit()

Хук page.exit() в библиотеке Page.js предназначен для выполнения логики при покидании текущего маршрута. Он позволяет перехватить момент перехода с одной страницы (роута) на другую и выполнить необходимые действия до окончательного завершения навигации.

Это особенно важно в случаях, когда требуется:

  • очистка ресурсов (например, удаление обработчиков событий)
  • сохранение состояния
  • отмена асинхронных операций
  • управление UI (скрытие модальных окон, сброс форм)

Общий синтаксис

page.exit(path, callback)

Параметры:

  • path — строка маршрута, с которого осуществляется выход
  • callback — функция, выполняемая при покидании маршрута

Базовый пример использования

page('/profile', function(ctx) {
  console.log('Вход в профиль');
});

page.exit('/profile', function(ctx) {
  console.log('Выход из профиля');
});

При переходе с /profile на любой другой маршрут будет вызван обработчик page.exit.


Контекст ctx в page.exit()

В колбэке доступен объект контекста ctx, аналогичный тому, что используется в обычных маршрутах:

page.exit('/dashboard', function(ctx) {
  console.log(ctx.path);
  console.log(ctx.params);
});

Это позволяет:

  • анализировать текущий маршрут
  • использовать параметры URL
  • работать с query-параметрами

Порядок выполнения

При переходе между маршрутами порядок выполнения следующий:

  1. Срабатывают exit-хуки текущего маршрута
  2. Выполняются middleware нового маршрута
  3. Вызывается основной обработчик нового маршрута

Это означает, что page.exit() выполняется до загрузки следующего маршрута, но после начала перехода.


Несколько обработчиков exit

Можно определить несколько обработчиков для одного маршрута:

page.exit('/settings', function(ctx) {
  console.log('Очистка данных');
});

page.exit('/settings', function(ctx) {
  console.log('Удаление событий');
});

Они будут выполнены последовательно в порядке объявления.


Асинхронные операции

page.exit() не поддерживает асинхронную блокировку перехода напрямую. Если в обработчике выполняется асинхронный код, переход всё равно произойдет без ожидания завершения:

page.exit('/editor', function(ctx) {
  setTimeout(() => {
    console.log('Сохранение завершено');
  }, 1000);
});

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

  • использовать флаги состояния
  • контролировать переход вручную

Очистка ресурсов

Частый сценарий — удаление слушателей событий:

function onResize() {
  console.log('resize');
}

page('/page', function() {
  window.addEventListener('resize', onResize);
});

page.exit('/page', function() {
  window.removeEventListener('resize', onResize);
});

Это предотвращает утечки памяти и дублирование обработчиков.


Работа с таймерами

let interval;

page('/timer', function() {
  interval = setInterval(() => {
    console.log('tick');
  }, 1000);
});

page.exit('/timer', function() {
  clearInterval(interval);
});

Без очистки таймер продолжит работать даже после смены маршрута.


Управление DOM

page('/modal', function() {
  document.getElementById('modal').style.display = 'block';
});

page.exit('/modal', function() {
  document.getElementById('modal').style.display = 'none';
});

page.exit() удобно использовать для сброса визуального состояния интерфейса.


Использование с динамическими маршрутами

page('/user/:id', function(ctx) {
  console.log('Просмотр пользователя', ctx.params.id);
});

page.exit('/user/:id', function(ctx) {
  console.log('Покидание пользователя', ctx.params.id);
});

Параметры маршрута доступны так же, как и в обычных обработчиках.


Отличие от middleware

В отличие от стандартных middleware-функций Page.js:

  • middleware работают при входе в маршрут
  • page.exit() — при выходе из маршрута

Это делает его аналогом “hook cleanup” в других архитектурах.


Частые ошибки

1. Ожидание асинхронного завершения

page.exit('/form', async function(ctx) {
  await saveData(); // не остановит переход
});

2. Неправильный путь

page.exit('form', function() {}); // ошибка — отсутствует /

3. Забытая очистка Отсутствие exit-хука при работе с:

  • событиями
  • интервалами
  • глобальными состояниями

Комбинирование с другими хуками

page('/chat', loadChat, renderChat);

page.exit('/chat', function() {
  disconnectWebSocket();
});

Здесь:

  • loadChat и renderChat — обработка входа
  • page.exit() — завершение соединения

Практический сценарий: SPA-редактор

let unsaved = false;

page('/editor', function() {
  window.addEventListener('input', () => {
    unsaved = true;
  });
});

page.exit('/editor', function() {
  if (unsaved) {
    console.log('Есть несохранённые изменения');
  }
});

В реальном приложении сюда может добавляться:

  • подтверждение пользователя
  • автосохранение
  • логирование

Ограничения

  • нельзя отменить переход напрямую
  • нет встроенного механизма ожидания
  • выполняется только при смене маршрута через Page.js (не при полной перезагрузке страницы)

Роль в архитектуре SPA

page.exit() играет ключевую роль в управлении жизненным циклом маршрутов:

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

Использование page.exit() позволяет приблизить поведение приложения к полноценной системе управления состоянием с чётким контролем за жизненным циклом компонентов.