Page.js — это небольшая, но мощная библиотека для управления маршрутизацией в одностраничных приложениях (SPA) на JavaScript. Основная задача Page.js — сопоставление URL и функций-обработчиков, что позволяет динамически изменять содержимое страницы без полной перезагрузки.
Регистрация маршрутов выполняется через функцию
page(path, callback), где path — строка
маршрута, а callback — функция, которая будет вызвана при
переходе на этот маршрут:
page('/', () => {
console.log('Главная страница');
});
page('/about', () => {
console.log('Страница о проекте');
});
page();
Вызов page() в конце инициализирует маршрутизацию,
отслеживая изменения URL и вызывая соответствующие обработчики.
Code splitting — это подход, позволяющий разбивать приложение на отдельные модули, загружаемые только по мере необходимости. В сочетании с Page.js это позволяет подгружать код конкретной страницы только при переходе на неё, снижая первоначальный размер бандла и ускоряя загрузку.
Простейший пример динамического импорта с использованием
import():
page('/dashboard', async () => {
const module = await import('./pages/dashboard.js');
module.renderDashboard();
});
Здесь код для страницы /dashboard не включается в
основной бандл. Он загружается только при переходе на маршрут
/dashboard. Это ключевой механизм для оптимизации
производительности SPA.
Page.js поддерживает асинхронные функции в качестве обработчиков. Это
позволяет использовать await внутри маршрутов для загрузки
компонентов, данных или других ресурсов:
page('/profile/:id', async (ctx) => {
const { default: renderProfile } = await import('./pages/profile.js');
const data = await fetch(`/api/user/${ctx.params.id}`).then(res => res.json());
renderProfile(data);
});
В данном примере:
ctx.params.id извлекает параметр маршрута
:id.fetch позволяет получить данные перед
рендерингом страницы.Такой подход снижает нагрузку на начальный бандл и делает приложение более отзывчивым.
Для более крупных приложений часто применяют стратегию ленивой загрузки (lazy loading). Можно создать отдельный модуль для каждой страницы:
// main.js
page('/', async () => {
const { default: renderHome } = await import('./pages/home.js');
renderHome();
});
page('/settings', async () => {
const { default: renderSettings } = await import('./pages/settings.js');
renderSettings();
});
page();
Преимущества такого подхода:
Page.js поддерживает middleware — функции, выполняющиеся до основного обработчика маршрута. Это удобно для авторизации, логирования или загрузки данных.
Пример с динамической загрузкой и middleware:
async function auth(ctx, next) {
const user = await fetch('/api/me').then(res => res.json());
if (!user.loggedIn) {
return page.redirect('/login');
}
ctx.user = user;
await next();
}
page('/dashboard', auth, async (ctx) => {
const { default: renderDashboard } = await import('./pages/dashboard.js');
renderDashboard(ctx.user);
});
page();
Здесь middleware auth проверяет, вошёл ли пользователь в
систему, а основной обработчик подгружает модуль только после успешной
проверки.
При использовании Page.js и динамических импортов важно правильно настраивать сборщик:
import().page('/reports', async () => {
const { default: renderReports } = await import(
/* webpackChunkName: "reports" */ './pages/reports.js'
);
renderReports();
});
Это создаст отдельный файл reports.[hash].js, который
будет подгружаться только при переходе на /reports.
При использовании динамического импорта важно обрабатывать возможные ошибки:
page('/analytics', async () => {
try {
const { default: renderAnalytics } = await import('./pages/analytics.js');
renderAnalytics();
} catch (error) {
console.error('Не удалось загрузить страницу аналитики', error);
page.redirect('/error');
}
});
Такой подход предотвращает падение всего приложения при проблемах с сетью или неправильной сборкой модуля.
import().Использование Page.js вместе с динамическим импортом создаёт гибкую, модульную архитектуру SPA, позволяя строить быстрые и масштабируемые веб-приложения с минимальными затратами на начальную загрузку.