При построении одностраничных приложений (SPA) часто возникает необходимость управлять доступом пользователей к определённым маршрутам. Библиотека Page.js предоставляет удобный механизм для организации редиректов на основе состояния авторизации.
Для начала создаётся базовая конфигурация маршрутов:
import page from 'page';
function isAuthenticated() {
return Boolean(localStorage.getItem('token'));
}
page('/', () => {
console.log('Главная страница');
});
page('/login', () => {
console.log('Страница логина');
});
page('/dashboard', (ctx, next) => {
if (!isAuthenticated()) {
page.redirect('/login');
} else {
console.log('Панель пользователя');
}
});
Ключевые моменты:
page.redirect('/login') — перенаправляет пользователя
на указанный маршрут.isAuthenticated())
должна быть лёгкой и быстрой, чтобы не блокировать рендеринг.Page.js поддерживает концепцию middleware, которая позволяет вставлять проверки перед обработкой маршрута.
function requireAuth(ctx, next) {
if (!isAuthenticated()) {
page.redirect('/login');
} else {
next();
}
}
page('/dashboard', requireAuth, () => {
console.log('Панель пользователя');
});
page('/settings', requireAuth, () => {
console.log('Настройки пользователя');
});
Преимущества middleware:
Для удобства пользователей часто нужно возвращать их на исходную страницу после авторизации. Для этого можно сохранять целевой путь до редиректа:
let redirectAfterLogin = '/';
page('/dashboard', (ctx, next) => {
if (!isAuthenticated()) {
redirectAfterLogin = ctx.path;
page.redirect('/login');
} else {
next();
}
});
page('/login', (ctx) => {
console.log('Форма логина');
// После успешного ввода
const loginButton = document.querySelector('#login-btn');
loginButton.addEventListener('click', () => {
localStorage.setItem('token', 'example-token');
page.redirect(redirectAfterLogin);
});
});
Особенности:
ctx.path содержит текущий URL, на который пытался
попасть пользователь.Разделение маршрутов на публичные и приватные повышает читаемость приложения:
const publicRoutes = ['/login', '/register'];
const privateRoutes = ['/dashboard', '/settings'];
page('*', (ctx, next) => {
if (!isAuthenticated() && privateRoutes.includes(ctx.path)) {
page.redirect('/login');
} else if (isAuthenticated() && publicRoutes.includes(ctx.path)) {
page.redirect('/dashboard');
} else {
next();
}
});
Выигрыш от такого подхода:
Page.js позволяет сохранять параметры маршрута и query string при редиректе:
page('/dashboard/:section', requireAuth, (ctx) => {
console.log('Текущий раздел:', ctx.params.section);
});
page('/login', (ctx) => {
const redirectTo = ctx.query.redirect || '/dashboard';
document.querySelector('#login-btn').addEventListener('click', () => {
localStorage.setItem('token', 'example-token');
page.redirect(redirectTo);
});
});
Особенности использования параметров:
ctx.params — содержит динамические сегменты
маршрута.ctx.query — позволяет работать с query string и
передавать дополнительные данные через URL.Иногда проверка авторизации требует запроса к серверу:
async function checkAuthServerSide() {
const token = localStorage.getItem('token');
if (!token) return false;
const response = await fetch('/api/validate-token', {
headers: { 'Authorization': `Bearer ${token}` }
});
return response.ok;
}
page('/dashboard', async (ctx, next) => {
const authenticated = await checkAuthServerSide();
if (!authenticated) {
page.redirect('/login');
} else {
next();
}
});
Важные моменты:
await позволяет дождаться результата
проверки перед рендерингом маршрута.Такая структура редиректов обеспечивает гибкий и безопасный контроль доступа в SPA с Page.js.