В основе работы библиотеки Page.js лежит механизм отслеживания изменений URL и сопоставления их с обработчиками маршрутов. Каждое изменение маршрута инициирует цепочку событий, которая проходит через несколько этапов: определение маршрута, выполнение middleware, вызов конечного обработчика и возможное прерывание цепочки.
Маршрут в Page.js — это не просто строка URL, а полноценная последовательность функций, каждая из которых получает контекст и управление потоком выполнения.
Каждое событие изменения маршрута сопровождается созданием объекта
контекста (ctx). Этот объект передаётся через всю цепочку
обработчиков и содержит ключевые данные:
ctx.path — текущий путьctx.params — параметры маршрутаctx.querystring — строка запросаctx.hash — якорь URLctx.state — состояние историиctx.title — заголовок страницы (если используется)Контекст является центральной точкой взаимодействия между обработчиками и позволяет передавать данные между middleware.
Page.js реализует концепцию middleware, аналогичную серверным фреймворкам. Каждый маршрут может содержать несколько функций:
page('/user/:id', loadUser, showUser);
Каждая функция имеет сигнатуру:
function(ctx, next) {
// логика
next();
}
ctx — объект контекстаnext — функция для передачи управления следующему
обработчикуЕсли next() не вызывается, цепочка останавливается.
Переход между маршрутами может происходить несколькими способами:
page('/path')page.show('/path')При каждом таком действии запускается процесс маршрутизации.
Page.js автоматически перехватывает клики по ссылкам
(<a>), если они:
target="_blank"При клике:
Программная навигация осуществляется через:
page('/about');
или
page.show('/about');
Разница:
page() — универсальный вызов (может регистрировать
маршрут или переходить)page.show() — явный переход к маршрутуПри вызове:
Page.js использует History API (pushState,
popstate), что позволяет:
Событие popstate автоматически обрабатывается
библиотекой:
window.addEventListener('popstate', handler);
При этом:
Page.js поддерживает глобальные middleware:
page('*', middleware);
Такие функции выполняются при каждом переходе, независимо от маршрута.
Пример:
page('*', function(ctx, next) {
console.log('Переход к:', ctx.path);
next();
});
Используется для:
Если middleware не вызывает next(), выполнение
останавливается:
page('/admin', function(ctx, next) {
if (!isAdmin()) return page('/login');
next();
});
Таким образом реализуется контроль доступа.
Page.js поддерживает асинхронные middleware:
page('/data', async function(ctx, next) {
ctx.data = await fetchData();
next();
});
Важно:
next() вызывается после завершения асинхронной
операцииnext() блокирует дальнейшее выполнениеPage.js не имеет встроенной системы обработки ошибок, но можно реализовать её через middleware:
page('*', async function(ctx, next) {
try {
await next();
} catch (err) {
console.error(err);
page('/error');
}
});
Перенаправление выполняется через:
page.redirect('/old', '/new');
или вручную:
page('/new');
При редиректе:
Параметры извлекаются автоматически:
page('/user/:id', function(ctx) {
console.log(ctx.params.id);
});
При переходе на /user/42:
ctx.params = { id: '42' }
Маршруты проверяются в порядке регистрации:
page('/user/:id', handler1);
page('/user/settings', handler2);
Проблема: /user/settings может быть перехвачен первым
маршрутом.
Решение — регистрировать более специфичные маршруты раньше:
page('/user/settings', handler2);
page('/user/:id', handler1);
После выполнения всех middleware:
Page.js позволяет передавать состояние:
page.show('/profile', { userId: 1 });
Доступ:
ctx.state.userId
Состояние сохраняется в History API.
Иногда требуется повторно выполнить обработчики текущего маршрута:
page.replace('/current');
Отличие:
Page.js поддерживает обработчики выхода из маршрута:
page.exit('/user/:id', function(ctx) {
console.log('Выход из профиля');
});
Они вызываются:
Полный порядок:
exit текущего маршрутаctx*)Page.js не управляет скроллом автоматически. Реализация:
page('*', function(ctx, next) {
window.scrollTo(0, 0);
next();
});
Можно отключить автоматический перехват:
page.start({ click: false });
Теперь переходы выполняются только вручную.
По умолчанию требуется запуск:
page();
или:
page.start();
Это инициирует:
Если маршрут не найден:
page('*', function(ctx) {
page('/404');
});
Или:
page('*', notFoundHandler);
Так как ctx создаётся заново, кеширование реализуется
вручную:
const cache = {};
page('/users', function(ctx, next) {
if (cache.users) {
ctx.users = cache.users;
return next();
}
fetchUsers().then(users => {
cache.users = users;
ctx.users = users;
next();
});
});
Middleware можно выносить:
function auth(ctx, next) {
if (!loggedIn()) return page('/login');
next();
}
page('/dashboard', auth, dashboardHandler);
Page.js не поддерживает вложенность напрямую, но её можно имитировать:
page('/admin', auth, adminLayout);
page('/admin/users', auth, usersHandler);
Общий middleware (auth) применяется вручную.
Смена маршрута в Page.js — это:
next()Такой подход делает поведение маршрутов предсказуемым и полностью контролируемым.