Оптимизация хуков

В Navigo хук — это функция, которая выполняется при срабатывании определённых событий маршрутизатора. Хуки позволяют внедрять дополнительную логику перед или после перехода на маршрут. Ключевыми типами хуков являются: before, after, error, notFound.

  • before — вызывается перед обработкой маршрута. Возвращаемое значение или вызов done() могут остановить или разрешить навигацию.
  • after — выполняется после успешной смены маршрута. Полезен для логирования или аналитики.
  • error — срабатывает при исключениях внутри маршрута.
  • notFound — вызывается, когда маршрута не существует.

Пример базового использования хуков:

const router = new Navigo('/');

router.on('/home', () => {
    console.log('Главная страница');
});

router.hooks({
    before: (done, params) => {
        console.log('Проверка перед маршрутом', params);
        done();
    },
    after: (params) => {
        console.log('Переход завершён', params);
    },
    notFound: () => {
        console.log('Страница не найдена');
    }
});

Оптимизация хуков

1. Минимизация лишних вычислений

Хуки вызываются при каждом переходе на маршрут, поэтому любые тяжёлые вычисления внутри них могут замедлять работу приложения. Следует:

  • Переносить ресурсоёмкие операции в отдельные сервисы или кешировать результаты.
  • Использовать мемоизацию для функций, которые возвращают одинаковый результат для одинаковых параметров.
let cachedData = null;

router.hooks({
    before: (done) => {
        if (!cachedData) {
            cachedData = fetchData(); // тяжёлая операция только один раз
        }
        done();
    }
});

2. Избегание избыточных вызовов

Хук before может срабатывать на маршрутах, где его выполнение не требуется. Для оптимизации можно фильтровать маршруты:

router.hooks({
    before: (done, params, route) => {
        if (route.name === 'home') {
            performHomeCheck();
        }
        done();
    }
});

3. Асинхронные хуки

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

router.hooks({
    before: async (done) => {
        try {
            await checkUserAuth();
            done();
        } catch {
            redirectToLogin();
        }
    }
});

4. Централизация логики

Если несколько маршрутов используют одинаковую проверку, имеет смысл вынести её в отдельную функцию. Это снижает дублирование и ускоряет поддержку кода:

function authGuard(done) {
    if (!isAuthenticated()) {
        redirectToLogin();
    } else {
        done();
    }
}

router.hooks({
    before: authGuard
});

5. Использование глобальных и локальных хуков

  • Глобальные хуки применяются ко всем маршрутам через router.hooks().
  • Локальные хуки задаются при создании маршрута через объект hooks:
router.on('/dashboard', {
    uses: dashboardHandler,
    hooks: {
        before: authGuard
    }
});

Комбинирование глобальных и локальных хуков позволяет экономно распределять вычисления, выполняя общие проверки глобально, а специфические — локально.

6. Сокращение частоты вызова хуков

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

  • Использовать флаги для контроля повторного вызова.
  • Проверять предыдущий маршрут и данные перед выполнением тяжёлой логики.
let lastRoute = null;

router.hooks({
    before: (done, params, route) => {
        if (lastRoute === route.url) {
            done(); // пропускаем повторное выполнение
        } else {
            lastRoute = route.url;
            loadRouteData(route).then(done);
        }
    }
});

7. Логирование и отладка

Для профилирования важно фиксировать время выполнения хуков:

router.hooks({
    before: (done, params, route) => {
        const start = performance.now();
        done();
        console.log(`Before-hook ${route.url} выполнен за ${performance.now() - start}ms`);
    }
});

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

8. Сочетание с ленивой загрузкой модулей

Хуки отлично подходят для динамической загрузки компонентов только при переходе на маршрут:

router.on('/profile', async () => {
    const module = await import('./profile.js');
    module.renderProfile();
});

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

9. Пакетная обработка

Если несколько хуков выполняют похожие действия, их можно объединять в цепочку, чтобы уменьшить накладные расходы на вызовы функций:

const hooksChain = [
    authGuard,
    logNavigation,
    prefetchData
];

router.hooks({
    before: (done, params, route) => {
        let i = 0;
        const next = () => {
            if (i < hooksChain.length) {
                hooksChain[i++](next, params, route);
            } else {
                done();
            }
        };
        next();
    }
});

Это позволяет поддерживать читаемую структуру кода и легко добавлять новые шаги без роста сложности.


Оптимизация хуков в Navigo критична для производительности SPA, особенно при множестве маршрутов и сложной логике загрузки данных. Правильное использование фильтрации, асинхронности, кеширования и централизованной логики обеспечивает быстрый отклик интерфейса и поддерживаемость кода.