Navigo — легковесная библиотека для маршрутизации на стороне клиента в JavaScript, предоставляющая гибкие возможности для управления URL и навигацией. Одним из ключевых механизмов, позволяющих строить сложные приложения с динамическим поведением, являются асинхронные хуки. Они дают возможность выполнять операции до и после активации маршрута, включая запросы к серверу, проверку авторизации, загрузку данных и подготовку контента.
Navigo поддерживает несколько типов хуков, которые могут быть асинхронными:
before — вызывается перед активацией маршрута. Позволяет остановить переход, выполнить проверки или подготовительные действия.
after — вызывается после успешного перехода на маршрут. Используется для логирования, анимаций или дополнительных действий после рендеринга страницы.
leave — вызывается при уходе с текущего маршрута. Часто применяется для сохранения состояния или подтверждения выхода.
Все эти хуки могут возвращать промисы, что позволяет интегрировать асинхронные операции без блокировки интерфейса.
Простейший пример before-хука с асинхронной проверкой авторизации:
const router = new Navigo('/', { hash: true });
router
.on('/dashboard', async (params, query) => {
console.log('Маршрут активирован', params, query);
})
.before(async (done, params, query) => {
try {
const user = await fetch('/api/auth/status').then(res => res.json());
if (user.authenticated) {
done(); // продолжаем переход
} else {
router.navigate('/login'); // перенаправление
}
} catch (error) {
console.error('Ошибка авторизации', error);
router.navigate('/error');
}
})
.resolve();
Ключевые моменты:
Хук before принимает функцию с аргументами
(done, params, query).
done — функция, вызываемая для продолжения маршрута
после асинхронной операции.Асинхронные операции выполняются через async/await
или промисы.
В случае необходимости блокировки маршрута можно вызвать
done(false) или перенаправление на другой маршрут.
После загрузки маршрута может потребоваться дополнительная асинхронная логика, например, подгрузка данных для отображения:
router
.on('/profile/:id', async (params) => {
const userData = await fetch(`/api/users/${params.id}`).then(res => res.json());
renderProfile(userData);
})
.after(async (params) => {
await analytics.trackPageView(`/profile/${params.id}`);
})
.resolve();
Особенности:
after-хук вызывается после успешного
выполнения обработчика маршрута.params и query доступны для
использования внутри хука.Асинхронный leave-хук полезен для предупреждения пользователя о несохранённых изменениях:
router
.on('/editor', () => {
renderEditor();
})
.leave(async (done) => {
const confirmLeave = await showUnsavedChangesDialog();
if (confirmLeave) {
done(); // покидаем маршрут
} else {
done(false); // остаёмся на текущем маршруте
}
})
.resolve();
Особенности:
leave-хук получает done как обязательный
аргумент.done(false) останавливает переход, позволяя
пользователю принять решение.Минимизировать блокировку интерфейса Асинхронные
операции в before должны выполняться быстро, чтобы не
замедлять навигацию. Если операция долгая, предусмотреть индикатор
загрузки.
Обработка ошибок Любые ошибки внутри хуков должны быть перехвачены и корректно обработаны — это предотвращает неконтролируемый сбой маршрутизации.
Разделение логики Не стоит помещать всю асинхронную логику внутрь одного хука. Лучше выделять отдельные функции для API-запросов, проверок и логирования.
Переиспользуемые хуки Для одинаковой проверки авторизации или состояния данных можно создавать функции-хуки и подключать их к нескольким маршрутам.
Комбинирование с динамическими маршрутами
Асинхронные хуки отлично работают с параметрами маршрутов
(/profile/:id), обеспечивая загрузку данных только для
конкретного контекста.
const router = new Navigo('/', { hash: true });
async function checkAuth(done) {
const user = await fetch('/api/auth/status').then(res => res.json());
if (user.authenticated) {
done();
} else {
router.navigate('/login');
}
}
async function loadUserData(params) {
return await fetch(`/api/users/${params.id}`).then(res => res.json());
}
router
.on('/profile/:id', async (params) => {
const data = await loadUserData(params);
renderProfile(data);
})
.before(checkAuth)
.after(async (params) => {
await analytics.trackPageView(`/profile/${params.id}`);
})
.leave(async (done) => {
const confirmLeave = await showUnsavedChangesDialog();
if (confirmLeave) done();
else done(false);
})
.resolve();
В этом примере продемонстрировано:
Асинхронные хуки в Navigo создают мощный инструмент для построения интерактивных SPA, позволяя гибко управлять навигацией, проверками, загрузкой данных и пользовательскими сценариями без блокировки интерфейса.