Для работы с History API в Universal Router
необходимо использовать объект history браузера. Обычно
применяются методы pushState, replaceState и
событие popstate для отслеживания навигации. Universal
Router не имеет встроенной зависимости от History API, но интеграция
реализуется через пользовательский объект, передаваемый в
маршрутизатор.
Пример создания маршрутизатора с использованием History API:
import UniversalRouter from 'universal-router';
const routes = [
{ path: '/', action: () => 'Главная страница' },
{ path: '/about', action: () => 'О нас' },
{ path: '/contact', action: () => 'Контакты' },
];
const router = new UniversalRouter(routes);
function navigate(path) {
history.pushState({}, '', path);
router.resolve({ pathname: path }).then(result => {
document.getElementById('app').innerText = result;
});
}
window.addEventListener('popstate', () => {
router.resolve({ pathname: window.location.pathname }).then(result => {
document.getElementById('app').innerText = result;
});
});
Ключевые моменты:
pushState добавляет новый элемент в историю
браузера без перезагрузки страницы.popstate срабатывает при нажатии кнопок «Назад»
или «Вперед».router.resolve() принимает объект с полем
pathname, что позволяет Universal Router определять текущий
маршрут.Universal Router поддерживает асинхронные функции в
action, что позволяет загружать данные динамически при
навигации:
const routes = [
{
path: '/user/:id',
async action(context) {
const response = await fetch(`/api/users/${context.params.id}`);
const user = await response.json();
return `Пользователь: ${user.name}`;
},
},
];
Здесь context.params.id автоматически извлекается из
URL. При переходе через pushState или popstate
Universal Router корректно вызывает асинхронный action и
возвращает результат.
Если приложение размещается не в корне сайта, нужно учитывать базовый путь. Universal Router позволяет использовать префикс при разрешении маршрута:
const basePath = '/app';
function navigate(path) {
const fullPath = basePath + path;
history.pushState({}, '', fullPath);
router.resolve({ pathname: fullPath }).then(render);
}
window.addEventListener('popstate', () => {
const path = window.location.pathname;
router.resolve({ pathname: path }).then(render);
});
Префикс автоматически добавляется при построении URL и при разрешении маршрутов. Это важно для SPA, размещённых в подкаталогах.
Методы pushState и replaceState позволяют
обновлять URL без перезагрузки. Разница между ними:
pushState добавляет новый элемент в историю, создавая
точку возврата.replaceState заменяет текущий элемент, не создавая
новой точки возврата.Пример использования replaceState с Universal
Router:
function updateQuery(query) {
const url = new URL(window.location);
url.searchParams.set('q', query);
history.replaceState({}, '', url);
router.resolve({ pathname: url.pathname, query: url.searchParams }).then(render);
}
Это полезно для фильтров, поисковых запросов и других интерактивных элементов без создания лишних записей в истории.
Universal Router позволяет реализовать логику проверки перед переходом. При работе с History API это особенно актуально для SPA:
const routes = [
{
path: '/protected',
async action(context) {
if (!context.user) {
navigate('/login');
return;
}
return 'Доступ разрешён';
},
},
];
Такой подход позволяет интегрировать проверку авторизации и другие условия до рендеринга маршрута, сохраняя совместимость с History API.
History API предоставляет событие popstate, которое
важно для синхронизации состояния приложения с URL:
window.addEventListener('popstate', event => {
router.resolve({ pathname: window.location.pathname }).then(render);
});
pushState или replaceState.router.resolve() при изменении URL
через History API, чтобы Universal Router корректно обработал
маршрут.basePath
для корректной генерации URL.pushState и replaceState, чтобы контролировать
точки возврата.Universal Router поддерживает маршрут по умолчанию для всех неопределённых путей. При использовании History API это важно для корректного отображения ошибок:
const router = new UniversalRouter([
{ path: '/', action: () => 'Главная' },
{ path: '(.*)', action: () => 'Страница не найдена' },
]);
Маршрут (.*) ловит все URL, которые не соответствуют
другим маршрутам, позволяя показать страницу 404 без перезагрузки.
Query-параметры можно передавать в resolve() через
объект query:
function navigateWithQuery(path, query) {
const url = new URL(window.location.origin + path);
Object.keys(query).forEach(key => url.searchParams.set(key, query[key]));
history.pushState({}, '', url);
router.resolve({ pathname: url.pathname, query: url.searchParams }).then(render);
}
context.query в маршруте содержит все query-параметры,
что позволяет динамически строить страницы на их основе.Эти возможности делают Universal Router гибким инструментом для построения одностраничных приложений с полной поддержкой History API и динамической навигации.