Передача управления через next

В Page.js маршрутизация строится на обработчиках маршрутов, которые вызываются при совпадении URL с заданным путем. Каждый обработчик получает два параметра: объект контекста ctx и функцию next. Функция next служит для передачи управления следующему подходящему обработчику. Понимание работы next критически важно для организации цепочек middleware, фильтрации запросов и контроля порядка выполнения логики маршрутов.


Синтаксис и поведение next

Функция next вызывается внутри обработчика маршрута следующим образом:

page('/profile', function(ctx, next) {
    console.log('Первый обработчик');
    next(); // Передаём управление следующему обработчику
});

page('/profile', function(ctx) {
    console.log('Второй обработчик');
});

В этом примере при переходе на /profile сначала выполняется первый обработчик, затем, после вызова next(), управление передаётся второму. Если next не вызывается, выполнение цепочки маршрутов прекращается, и следующие обработчики не вызываются.

Ключевые моменты:

  • next() продолжает выполнение цепочки middleware для текущего маршрута.
  • next можно вызывать асинхронно, например, после выполнения промиса.
  • Пропуск вызова next — стандартный способ остановки дальнейшей обработки.

Использование нескольких middleware для одного маршрута

Page.js поддерживает несколько обработчиков для одного маршрута. Middleware могут фильтровать или модифицировать контекст перед передачей управления:

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

function logMiddleware(ctx, next) {
    console.log(`Пользователь зашёл на ${ctx.path}`);
    next();
}

page('/dashboard', authMiddleware, logMiddleware, function(ctx) {
    console.log('Основной обработчик страницы');
});

Принципы работы:

  1. Middleware вызываются последовательно в том порядке, в котором они передаются.
  2. Если один из middleware не вызовет next(), цепочка останавливается.
  3. Middleware может как блокировать доступ (например, через редирект), так и просто модифицировать контекст.

Асинхронные операции и next

next можно использовать после асинхронных операций, что позволяет строить цепочки обработки с задержкой:

page('/data', async (ctx, next) => {
    ctx.data = await fetchDataFromServer();
    next();
});

page('/data', (ctx) => {
    console.log('Данные загружены:', ctx.data);
});

Здесь первый обработчик загружает данные и сохраняет их в контексте ctx. После завершения загрузки вызывается next(), и второй обработчик получает уже подготовленный контекст. Это ключевой паттерн при работе с асинхронными данными в SPA.


Вложенные маршруты и использование next

next также полезен для вложенных маршрутов. Можно создавать обработчики, которые срабатывают на общую часть пути, а затем передавать управление конкретному маршруту:

page('/users', (ctx, next) => {
    console.log('Общий обработчик для всех пользователей');
    next();
});

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

Переход на /users/42 сначала вызовет общий обработчик, а затем — конкретный. Такой подход позволяет централизованно обрабатывать авторизацию, логирование или настройку контекста для группы маршрутов.


Передача управления в цепочке маршрутов с условиями

Функция next может вызываться условно, что позволяет реализовать гибкие цепочки обработки:

page('/shop', (ctx, next) => {
    if (ctx.query.showPromo) {
        console.log('Показ промо');
        next();
    }
});

page('/shop', (ctx) => {
    console.log('Основной контент магазина');
});

Если условие ctx.query.showPromo истинно, управление передается следующему обработчику, иначе — цепочка останавливается.


Совмещение next с редиректами и завершением маршрута

Редиректы и остановка маршрута взаимодействуют с next особым образом:

page('/admin', (ctx, next) => {
    if (!ctx.user.isAdmin) {
        page.redirect('/login'); // Не вызывает next, цепочка прекращается
    } else {
        next(); // Админ, продолжаем
    }
});

page('/admin', (ctx) => {
    console.log('Админская панель');
});

Редирект автоматически прерывает текущую цепочку. Вызов next() после редиректа не имеет смысла и может вызвать нежелательное поведение.


Практические рекомендации

  • Всегда явно вызывать next(), если обработчик не должен завершать маршрут.
  • Использовать next() для асинхронной подготовки данных перед основным обработчиком.
  • Для групп маршрутов применять общий middleware для логирования, проверки авторизации или модификации контекста.
  • Комбинировать условные вызовы next с редиректами для гибкого управления доступом к страницам.
  • Помнить, что порядок передачи middleware критичен: первый вызванный обработчик может блокировать последующие, если не вызовет next.

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