В клиентской маршрутизации важным аспектом является контроль над переходами между маршрутами. Библиотека Page.js предоставляет гибкие инструменты для управления этим процессом, включая возможность отмены перехода до его завершения.
Отмена перехода позволяет:
Каждый маршрут в Page.js представляет собой цепочку middleware-функций. Эти функции выполняются последовательно при переходе по URL.
Пример:
page('/profile', checkAuth, loadProfile, renderProfile);
Каждая функция получает параметры:
function middleware(ctx, next) {
// логика
next();
}
ctx — объект контекста (содержит путь, параметры,
состояние);next — функция для передачи управления следующему
middleware.Ключевая идея: если next() не вызывается,
переход останавливается.
Самый базовый способ отмены перехода — не вызывать
next():
function blockRoute(ctx, next) {
console.log('Переход запрещён');
}
page('/admin', blockRoute, renderAdmin);
В этом случае:
renderAdmin никогда не выполнится;Часто вместо полной остановки требуется перенаправление:
function requireAuth(ctx, next) {
if (!user.isAuthenticated) {
page('/login');
return;
}
next();
}
Особенности:
page.redirectБиблиотека предоставляет встроенный способ редиректа:
page('/old-route', () => {
page.redirect('/new-route');
});
или:
page.redirect('/old-route', '/new-route');
Это декларативный способ отмены перехода с заменой маршрута.
Page.js не имеет встроенной поддержки async/await, но
позволяет реализовать асинхронную логику вручную:
function checkPermission(ctx, next) {
fetch('/api/permission')
.then(res => res.json())
.then(data => {
if (data.allowed) {
next();
} else {
page('/forbidden');
}
});
}
Важно:
next() вызывается только после завершения асинхронной
операции;Распространённый сценарий — предупреждение пользователя:
function preventDataLoss(ctx, next) {
if (form.isDirty) {
const confirmLeave = window.confirm('Есть несохранённые изменения. Продолжить?');
if (!confirmLeave) return;
}
next();
}
Особенности:
Page.js позволяет регистрировать middleware для всех маршрутов:
page('*', globalGuard);
function globalGuard(ctx, next) {
if (!app.isReady) {
return;
}
next();
}
Применение:
Отмена перехода может сопровождаться обработкой ошибок:
function validateRoute(ctx, next) {
if (!ctx.params.id) {
throw new Error('Invalid route');
}
next();
}
Page.js поддерживает обработчик ошибок:
page('/user/:id', validateRoute, renderUser);
page('*', (ctx) => {
console.error('Ошибка маршрута');
});
ctx.state) для контроля переходаОбъект ctx может хранить пользовательские данные:
function checkState(ctx, next) {
if (ctx.state.blocked) {
return;
}
next();
}
Это позволяет:
Если переход инициирован через page():
page('/settings');
и в middleware не вызывается next(), происходит:
Для предотвращения изменения URL требуется контролировать момент
вызова page().
Распространённая архитектура:
function guard(ctx, next) {
if (!user) {
page('/login');
return;
}
next();
}
page('/dashboard', guard, renderDashboard);
Преимущества:
page('/secure',
checkAuth,
checkSubscription,
checkPermissions,
renderSecurePage
);
Любая функция может остановить переход:
function checkSubscription(ctx, next) {
if (!user.hasSubscription) {
page('/subscribe');
return;
}
next();
}
При остановке маршрута важно учитывать:
Рекомендуется:
next();Полезные техники:
function debug(ctx, next) {
console.log('Переход к:', ctx.path);
next();
}
Логирование перед потенциальной отменой:
function guard(ctx, next) {
console.log('Проверка доступа');
if (!user) {
console.warn('Доступ запрещён');
return;
}
next();
}
function cacheGuard(ctx, next) {
if (cache.has(ctx.path)) {
render(cache.get(ctx.path));
return;
}
next();
}
function lazyLoad(ctx, next) {
import('./page.js').then(module => {
module.render();
});
}
Здесь next() может не использоваться вовсе, полностью
заменяя цепочку.
Page.js работает поверх History API браузера. При отмене перехода:
Для контроля:
window.addEventListener('popstate', (e) => {
console.log('Навигация назад/вперёд');
});
next()1. Забытый return после редиректа
if (!user) {
page('/login');
}
next(); // ошибка
Правильно:
if (!user) {
page('/login');
return;
}
next();
2. Асинхронный код без контроля
fetch('/api').then(() => next());
next(); // дублирование
3. Смешивание логики рендера и проверки
Лучше разделять:
page('/route', check, render);
Page.js не имеет встроенной системы вложенных маршрутов, но можно имитировать:
page('/admin/*', adminGuard);
page('/admin/users', renderUsers);
Если adminGuard не вызывает next(), все
вложенные маршруты блокируются.
next() в
управлении переходомnext() — ключевой механизм:
Отсутствие вызова = фактическая отмена перехода.
Процесс перехода в Page.js:
Инициируется навигация
Формируется ctx
Запускается цепочка middleware
Каждый шаг решает:
next())page())Контроль над переходом полностью сосредоточен в middleware, что делает систему гибкой и предсказуемой при правильной архитектуре.