Page.js — это лёгкая клиентская библиотека для управления маршрутами в одностраничных приложениях на JavaScript. Одной из ключевых возможностей является композиция обработчиков, которая позволяет организовать логику маршрутов модульно и гибко.
В Page.js можно назначить несколько функций-обработчиков для одного
маршрута. Эти функции вызываются последовательно, передавая управление
от одного обработчика к следующему с помощью аргумента
next. Это создаёт цепочку обработчиков, аналогичную
middleware в Express.
page('/dashboard', authGuard, loadUserData, renderDashboard);
function authGuard(ctx, next) {
if (!ctx.user) {
page('/login');
} else {
next();
}
}
function loadUserData(ctx, next) {
fetch(`/api/users/${ctx.user.id}`)
.then(res => res.json())
.then(data => {
ctx.userData = data;
next();
});
}
function renderDashboard(ctx) {
document.body.innerHTML = `<h1>Привет, ${ctx.userData.name}</h1>`;
}
Ключевые моменты:
ctx — объект контекста маршрута, который передаётся
каждому обработчику.next — функция, вызываемая для передачи управления
следующему обработчику.authGuard,
затем loadUserData, и только после этого рендерится
интерфейс.Page.js позволяет использовать обработчики как на уровне отдельных маршрутов, так и глобально для всех маршрутов.
Глобальный middleware:
page('*', logNavigation);
function logNavigation(ctx, next) {
console.log('Переход на:', ctx.path);
next();
}
'*' обозначает все маршруты.Обработчики могут быть асинхронными. Для корректного продолжения
цепочки необходимо вызывать next() после завершения
асинхронной операции.
page('/profile', asyncLoadProfile, renderProfile);
async function asyncLoadProfile(ctx, next) {
try {
const res = await fetch(`/api/profile/${ctx.user.id}`);
ctx.profile = await res.json();
next();
} catch (error) {
page('/error');
}
}
function renderProfile(ctx) {
document.body.innerHTML = `<h2>${ctx.profile.fullName}</h2>`;
}
Особенности:
asyncLoadProfile не блокирует выполнение
других маршрутов, но гарантирует, что renderProfile
выполнится только после успешной загрузки данных./error или вызывая специальный middleware для обработки
исключений.nextВ Page.js можно условно прерывать цепочку обработчиков. Например, при отсутствии данных или прав доступа:
function adminGuard(ctx, next) {
if (!ctx.user.isAdmin) {
page('/unauthorized');
return; // прерывание цепочки
}
next(); // продолжение, если проверка успешна
}
next() остановит
обработку маршрута.Можно создавать сложные маршруты с повторно используемыми middleware:
const authMiddlewares = [authGuard, loadUserData];
page('/dashboard', ...authMiddlewares, renderDashboard);
page('/profile', ...authMiddlewares, renderProfile);
ctx является ключевым инструментом композиции
обработчиков:
page('/settings', authGuard, loadSettings, renderSettings);
function loadSettings(ctx, next) {
ctx.settings = { theme: 'dark', notifications: true };
next();
}
function renderSettings(ctx) {
document.body.innerHTML = `
<h3>Тема: ${ctx.settings.theme}</h3>
<p>Уведомления: ${ctx.settings.notifications ? 'Включены' : 'Выключены'}</p>
`;
}
Обработчики можно подключать динамически в зависимости от условий:
page('/dashboard', ctx => {
const middlewares = [authGuard];
if (ctx.user.isAdmin) middlewares.push(loadAdminData);
middlewares.push(renderDashboard);
page.compose(...middlewares)(ctx);
});
ctx и next позволяют передавать данные и
управление между обработчиками.next().Эти приёмы делают Page.js мощным инструментом для построения модульных и масштабируемых одностраничных приложений, позволяя управлять маршрутизацией и состоянием с минимальными усилиями.