Подписка на события навигации

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


События навигации и их роль

Page.js управляет навигацией через историю браузера и обеспечивает работу с URL без перезагрузки страницы. Основные события навигации, на которые можно подписаться:

  1. before — вызывается перед выполнением маршрута. Позволяет отменить переход или подготовить приложение к переходу.
  2. enter / leave — используются для выполнения логики при входе на маршрут и при выходе с маршрута.
  3. update — вызывается при изменении URL, но при этом текущий маршрут остаётся тем же. Полезно для работы с query-параметрами или якорями.
  4. error — вызывается при отсутствии соответствующего маршрута или при ошибках в обработчике.

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


Использование глобальных обработчиков

Page.js предоставляет методы для подписки на глобальные события навигации. Основной метод:

page('*', (ctx, next) => {
    console.log('Переход на любой маршрут:', ctx.path);
    next();
});

Здесь '*' означает, что обработчик будет вызван для всех маршрутов. Параметр ctx содержит информацию о текущем переходе:

  • ctx.path — полный путь URL;
  • ctx.pathname — путь без query-параметров;
  • ctx.querystring — строка запроса;
  • ctx.params — параметры маршрута;
  • ctx.state — объект состояния для передачи данных между маршрутами.

Метод next() позволяет передать управление следующему обработчику, если он есть.


Обработка переходов перед маршрутом (before)

Для выполнения проверок перед переходом используется вызов middleware. Например, проверка авторизации:

function requireAuth(ctx, next) {
    if (!ctx.state.user) {
        page.redirect('/login');
    } else {
        next();
    }
}

page('/dashboard', requireAuth, (ctx) => {
    console.log('Пользователь на дашборде');
});

Обработчик requireAuth вызывается перед основным обработчиком маршрута. Если пользователь не авторизован, выполняется редирект, иначе переход продолжается с вызовом next().


Вход и выход с маршрутов (enter / leave)

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

page('/profile', (ctx, next) => {
    console.log('Входим на профиль');
    next();
}, (ctx) => {
    console.log('Выходим с профиля');
});

Более гибко можно использовать отдельные middleware для enter и leave, чтобы управлять жизненным циклом страницы, например:

  • Инициализация данных;
  • Очистка ресурсов при уходе со страницы;
  • Управление визуальными эффектами.

Подписка на обновления URL без изменения маршрута

Событие update позволяет реагировать на изменения query-параметров или hash в URL без смены основного маршрута:

page('/search', (ctx, next) => {
    console.log('Поиск:', ctx.querystring);
});

page('*', (ctx, next) => {
    if (ctx.path.includes('?')) {
        console.log('Обновление URL с query:', ctx.querystring);
    }
    next();
});

Это полезно для SPA, где одна и та же страница может отображать разные данные в зависимости от параметров URL.


Обработка ошибок и неопределённых маршрутов

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

page('*', (ctx) => {
    console.log('Страница не найдена:', ctx.path);
    document.body.innerHTML = '<h1>404 - Not Found</h1>';
});

Можно также подписаться на ошибочные переходы с помощью собственного middleware:

page('/admin', (ctx, next) => {
    if (!ctx.state.user || !ctx.state.user.isAdmin) {
        ctx.throw = 'Нет доступа';
    } else {
        next();
    }
});

page('*', (ctx) => {
    if (ctx.throw) {
        console.error('Ошибка навигации:', ctx.throw);
    }
});

Интеграция с состоянием приложения

Подписка на события навигации часто используется для синхронизации состояния SPA с URL. Например, хранение текущей вкладки или фильтров:

page('/products', (ctx) => {
    const params = new URLSearchParams(ctx.querystring);
    const category = params.get('category') || 'all';
    renderProducts(category);
});

function renderProducts(category) {
    console.log('Отображаем категорию:', category);
}

Использование ctx.querystring и middleware позволяет динамически подгружать данные без перезагрузки страницы.


Рекомендации по организации подписок

  1. Минимизировать количество глобальных обработчиков — слишком много middleware для всех маршрутов может замедлить навигацию.
  2. Использовать цепочку next() для middleware — обеспечивает контроль над последовательностью обработки.
  3. Разделять логику входа и выхода — упрощает управление ресурсами и анимациями.
  4. Обрабатывать ошибки централизованно — упрощает логирование и вывод сообщений пользователю.
  5. Синхронизировать состояние с URL — query-параметры и hash должны отражать текущую визуализацию страницы.

Итоговые возможности подписки

  • Глобальные middleware для всех маршрутов;
  • Локальные middleware для отдельных маршрутов;
  • Обработка before, enter, leave, update, error;
  • Доступ к контексту перехода через объект ctx;
  • Управление редиректами и отменой переходов;
  • Синхронизация состояния SPA с URL.

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