Разделение логики роутинга

Библиотека Page.js предоставляет легковесный способ организации клиентской маршрутизации в одностраничных приложениях (SPA). Основная задача — сопоставление URL с функциями-обработчиками и управление переходами между “страницами” без перезагрузки браузера.

В Page.js ключевым элементом является функция page(path, callback), где:

  • path — строка, описывающая маршрут. Может содержать параметры, например '/user/:id'.
  • callback — функция, которая выполняется при совпадении URL с маршрутом.
page('/about', () => {
  console.log('Страница о нас');
});

page('/user/:id', ctx => {
  console.log('ID пользователя:', ctx.params.id);
});

Контекст ctx содержит информацию о текущем маршруте: параметры, query-строку и путь.


Разделение логики роутинга

В больших приложениях роутинг становится громоздким, если хранить все маршруты в одном файле. Разделение логики роутинга на модули помогает структурировать код и упростить поддержку.

Модульная организация маршрутов

Можно создать отдельные файлы для каждого блока приложения, например:

  • routes/home.js — домашняя страница
  • routes/user.js — страницы пользователей
  • routes/admin.js — административные маршруты

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

// routes/home.js
export function homeRoutes() {
  page('/', () => {
    renderHomePage();
  });

  page('/about', () => {
    renderAboutPage();
  });
}

// routes/user.js
export function userRoutes() {
  page('/user/:id', ctx => {
    renderUserPage(ctx.params.id);
  });

  page('/user/:id/edit', ctx => {
    renderEditUserPage(ctx.params.id);
  });
}

Главный файл приложения импортирует все модули и инициализирует маршруты:

import { homeRoutes } from './routes/home.js';
import { userRoutes } from './routes/user.js';

homeRoutes();
userRoutes();

page();

Использование middleware для общих операций

Page.js поддерживает цепочки middleware, аналогично Express.js. Middleware полезны для выполнения общих действий перед обработкой конкретного маршрута: проверка авторизации, логирование, установка данных страницы.

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

page('/admin', authMiddleware, () => {
  renderAdminDashboard();
});

Middleware можно применять глобально:

page('*', (ctx, next) => {
  console.log('Переход на:', ctx.path);
  next();
});

Параметры маршрута и query-строка

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

page('/post/:postId', ctx => {
  const postId = ctx.params.postId;
  console.log('Показ поста:', postId);
});

Query-параметры извлекаются из ctx.querystring или через URLSearchParams:

page('/search', ctx => {
  const params = new URLSearchParams(ctx.querystring);
  const query = params.get('q');
  console.log('Поиск по:', query);
});

Ленивая загрузка модулей

Для крупных приложений выгодно загружать маршруты динамически, только когда они нужны. Используется import():

page('/admin', async () => {
  const module = await import('./routes/admin.js');
  module.adminRoutes();
});

Так достигается уменьшение размера основного бандла и ускорение загрузки.


Навигация и контроль переходов

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

  • page.show(path) — переход на путь без перезагрузки страницы
  • page.redirect(path) — переход с заменой текущей записи в истории
document.querySelector('#go-home').addEventListener('click', () => {
  page.show('/');
});

Можно управлять историей браузера и отслеживать события popstate для интеграции с кнопками «назад/вперед».


Обработка ошибок и fallback-маршруты

Для несуществующих маршрутов можно создать fallback:

page('*', () => {
  renderNotFoundPage();
});

Важно располагать такой маршрут в конце, чтобы он не перехватывал валидные URL.


Стратегия организации больших приложений

  1. Разделение на модули — отдельный файл для каждой логической группы страниц.
  2. Middleware для повторяющихся операций — авторизация, логирование, настройка состояния.
  3. Lazy-loading — динамическая подгрузка маршрутов для сокращения времени загрузки.
  4. Fallback-маршруты и обработка ошибок — единообразная обработка несуществующих страниц.
  5. Централизованный вызов page() — после регистрации всех маршрутов, чтобы избежать дублирования.

Интеграция с рендерингом

Page.js не занимается отображением, поэтому маршруты связываются с функциями рендеринга компонентов или шаблонов:

function renderHomePage() {
  document.body.innerHTML = '<h1>Главная</h1>';
}

function renderUserPage(id) {
  document.body.innerHTML = `<h1>Пользователь ${id}</h1>`;
}

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


Хотите, я могу сразу написать примеры структурированных проектов с Page.js, где показано разделение на модули, middleware и lazy-loading, с полностью рабочим кодом? Это будет как учебный проект для практики.