SPA Routing
SPA Routing - это когда навигация по приложению происходит полностью внутри браузера, без запросов на сервер за новой HTML-страницей при каждом клике.
Как это работает
-
Сервер при первом запросе отдаёт один-единственный index.html
В нём - <div id="root"></div> и ссылка на огромный js-бандл -
JavaScript загружается и сразу читает текущий адрес:
window.location.pathname > /profile/settings -
На основании пути роутер решает, какой компонент показать(чаще всего это дерево маршрутов)
-
При клике по ссылке происходит примерно следующее:
function handleClick(e) {
e.preventDefault(); // ← самое важное
const path = e.currentTarget.getAttribute("href");
window.history.pushState({}, "", path); // меняем URL
// дальше либо setState, либо подписка на popstate / событие роутера
// → приложение перерисовывает нужный кусок
}
- Браузерная кнопка «назад» вызывает событие popstate > роутер снова читает location.pathname и перерисовывает экран
Именно поэтому в хорошем SPA при нажатии «назад» всё выглядит плавно и состояние восстанавливается.
Что использовать для настройки?
- React Router - классика для реакт приложений
- Next.js App Router - когда нужен и SSR, и клиентский роутинг, и file-based routing
- Vue Router, SvelteKit routing, SolidStart, Astro + view transitions - для каждого фреймворка свой раутер
С какими проблемами можно столкнуться?
-
Сервер не знает про клиентские маршруты
> заходишь по прямой ссылке /users/948302 > 404
Решение: на сервере (nginx / express / vercel / netlify) настроить, чтобы любой нестатический путь возвращал 404page -
Потеря состояния при рефреше
> был открыт аккордеон, выбран фильтр, прокручена страница > F5 > всё слетело
Решение: сохранять в URL (search params), localStorage, IndexedDB или в глобальном состоянии с персистенсом -
Дублирующиеся запросы при «назад» / «вперёд»
> нажал назад > опять полетел запрос за теми же данными
Решение: кэширование (React Query, SWR, TanStack Query, next/cache) -
Scroll restoration ломается
> после возврата назад страница не прокручивается туда, где была
Решение: вручную сохранять scroll position или использовать window.history.scrollRestoration = "manual" -
Бесконечные редиректы / зацикливание
> ProtectedRoute > Login > back to protected > redirect loop
Часто из-за неправильной проверки isAuthenticated в сочетании с useEffect
Безопасность
Клиентский роутинг не даёт никакой защиты - это чисто UX-механизм. Всё, что происходит на фронте, пользователь может изменить.
Самые опасные ловушки:
- Фейковые route guards на клиенте
if (localStorage.getItem("role") !== "admin") {
navigate("/no-access");
}
// любой в devtools меняет localStorage и заходит куда угодно
Решение: Используйте «безопасные данные» или проверки на беке. Проверка должна производиться только с данными из стейта, которые нельзя поменять
- Открытые данные в бандле
Весь код SPA скачивается пользователю. Часто в нём лежит:- структура всех роутов (включая админские)
- endpoint’ы API
- иногда даже чувствительная логика или тестовые токены😁
Решение: code splitting + server-side role-based chunk loading (очень редко реализуют)
- Манипуляция URL и history API
Злоумышленник может подменить pathname вручную > если данные грузятся по пути без проверки на бэке > утечка данных
Routing это не просто смена компонентов по путям, но также:
- синхронизации URL и стейта
- предзагрузка данных
- управление скроллом
- обработка ошибок и fallback’ов
- кэширование
- красивые переходы
- валидации переходов
Чтож, если есть какие-то интересные кейсы, то пишите в комменты 💬