Что изменилось в стандарте
В браузерах появился базовый уровень поддержки Navigation API, что делает его доступным сразу после установки. Эта спецификация предоставляет разработчикам единый и предсказуемый способ управлять переходами между страницами, заменяя традиционные методы, такие как window.location, history.pushState и события popstate. Теперь API входит в базовый набор функций браузеров, а не остаётся экспериментальной фичей, требующей включения флагов или полифилов.
Основные возможности
Управление очередью переходов
Navigation API вводит объект navigation, содержащий очередь запросов на переход. Каждый запрос представляется как NavigationTransition, в котором хранится информация о целевом URL, типе перехода (например, navigate, reload, replace) и статусе выполнения. Очередь гарантирует, что переходы обрабатываются последовательно, исключая гонки, которые часто возникают при одновременных вызовах pushState.
Промисы вместо колбэков
Вместо события popstate теперь используется промис navigation.navigate(), который возвращает NavigationTransition. Разработчик может await завершения перехода, получить доступ к committed и finished состояниям, а также отловить ошибки через catch. Это упрощает асинхронный код и делает его более читаемым:
try {
const transition = await navigation.navigate('/dashboard');
await transition.finished;
// переход завершён, можно обновлять UI
} catch (e) {
// обработка отказа навигации
}
Интеграция с загрузкой ресурсов
API предоставляет событие navigation.onnavigate, которое вызывается перед началом перехода. В обработчике можно выполнить предзагрузку данных, отменить переход или изменить целевой URL. Поскольку переход ещё не начался, можно избежать лишних запросов к серверу, если, например, пользователь уже находится на нужной странице.
Управление историей без перезагрузки
Метод navigation.reload() заменяет традиционный location.reload(), но позволяет контролировать процесс перезагрузки через те же промисы. Аналогично, navigation.replace() меняет текущий URL без добавления новой записи в стек истории, сохраняя при этом все преимущества очереди переходов.
Преимущества для одностраничных приложений
Устранение «потерянных» состояний
В SPA часто возникают ситуации, когда быстрые последовательные переходы приводят к тому, что старый запрос завершает обновление UI после более нового, выводя пользовательский интерфейс в неконсистентное состояние. Navigation API решает эту проблему, гарантируя, что каждый переход будет завершён полностью перед началом следующего. Благодаря встроенному механизму отмены (transition.abort()), можно прекратить устаревшие запросы и освободить ресурсы.
Упрощённый код роутинга
Традиционные роутеры полагаются на комбинацию pushState, popstate и собственных механизмов очереди. С Navigation API роутер может работать полностью на промисах, избавляясь от глобального состояния и сложных подписок. Это упрощает тестирование: каждый переход можно изолировать в отдельный тест, проверяя только его результат.
Улучшенная поддержка сервер‑рендеринга
При сервер‑рендеринге часто требуется синхронно собрать данные до отправки HTML. Navigation API позволяет выполнить предварительную загрузку в onnavigate, а затем выполнить transition.finished только после того, как сервер вернёт готовый документ. Это делает процесс более предсказуемым и уменьшает количество «мигранных» состояний при первой загрузке страницы.
Совместимость и миграция
API уже реализован в основных браузерах на базе Chromium и Firefox. Для старых версий можно подключить полифил, но его использование не требуется в новых проектах. При миграции с pushState следует заменить все вызовы на navigation.navigate() и перенести обработчики popstate в navigation.onnavigate. При этом сохраняются возможности работы с историей, так как navigation.back() и navigation.forward() полностью совместимы с существующим стеком.
Практический пример
Рассмотрим простой роутер, который загружает страницы по маршрутам /home, /profile и /settings:
const routes = {
'/home': () => import('./pages/Home.js'),
'/profile': () => import('./pages/Profile.js'),
'/settings': () => import('./pages/Settings.js')
};
navigation.onnavigate = async (event) => {
const { destination } = event;
const loader = routes[destination.url.pathname];
if (!loader) {
// fallback to 404
await navigation.navigate('/404');
return;
}
const module = await loader();
await navigation.transition.finished;
document.body.replaceWith(module.default());
};
document.querySelectorAll('a[data-nav]').forEach(link => {
link.addEventListener('click', async (e) => {
e.preventDefault();
await navigation.navigate(link.href);
});
});
Код демонстрирует, как один обработчик onnavigate управляет загрузкой модулей, а ссылки используют navigation.navigate вместо обычного перехода. Благодаря очереди переходов, даже при быстром клике по нескольким ссылкам пользователь всегда увидит актуальное содержимое.
Итоги
Введение базового уровня Navigation API меняет подход к навигации в одностраничных приложениях. Очередь переходов, промис‑ориентированный интерфейс и возможность предзагрузки данных делают процесс более надёжным и простым в обслуживании. Разработчики могут избавиться от громоздкого кода, основанного на pushState и popstate, и построить более предсказуемую архитектуру роутинга, готовую к будущим улучшениям веб‑стека.