History API и hash-routing
History API и hash-routing — два механизма клиентской навигации в SPA: первый меняет URL через pushState без перезагрузки, второй использует якорь (#) в URL для хранения маршрута.
Зачем нужно
Любой SPA-роутер строится поверх одного из этих механизмов. Понимание разницы объясняет, почему react-router предлагает BrowserRouter (History API) и HashRouter (hash-routing), в каких ситуациях каждый из них применим и как настроить сервер для корректной работы.
Где используется
- History API / BrowserRouter: большинство современных SPA на хостингах с поддержкой fallback (Vercel, Netlify, Nginx с
try_files) - HashRouter: статические хостинги без server-side роутинга (GitHub Pages, S3 без CloudFront)
- MemoryRouter (react-router): тестирование и React Native — нет URL вообще
History API
// pushState — добавляет запись в историю
// history.pushState(state, title, url)
history.pushState({ page: 'about' }, '', '/about');
// replaceState — заменяет текущую запись
history.replaceState({ page: 'home' }, '', '/');
// Навигация назад/вперёд
history.back;
history.forward;
history.go(-2); // на 2 шага назад
// Событие при кнопках Назад/Вперёд
window.addEventListener('popstate', (event) => {
console.log('Путь изменился:', window.location.pathname);
console.log('State:', event.state);
// Перерендерить компонент для нового пути
renderPage(window.location.pathname);
});
BrowserRouter (react-router)
import { BrowserRouter, Routes, Route, Link, useNavigate } from 'react-router-dom';
function App() {
return (
// BrowserRouter использует History API под капотом
<BrowserRouter>
<nav>
<Link to="/">Главная</Link>
<Link to="/about">О нас</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/:id" element={<UserProfile />} />
</Routes>
</BrowserRouter>
);
}
Hash-routing
Hash (#) в URL не отправляется на сервер — браузер обрабатывает его полностью на клиенте:
https://example.com/#/about
https://example.com/#/users/42
// Изменение hash
window.location.hash = '#/about';
// Событие при изменении hash
window.addEventListener('hashchange', () => {
const path = window.location.hash.slice(1); // убираем '#'
renderPage(path);
});
// Получить текущий маршрут
const currentPath = window.location.hash.slice(1) || '/';
HashRouter (react-router)
import { HashRouter } from 'react-router-dom';
// URL будет выглядеть как: example.com/#/about
// Подходит для GitHub Pages и других статических хостингов
function App() {
return (
<HashRouter>
{/* ... */}
</HashRouter>
);
}
Сравнение
| Критерий | History API | Hash-routing |
|---|---|---|
| URL | /about |
/#/about |
| SEO | Хорошее | Плохое (# игнорируется) |
| Настройка сервера | Нужен fallback | Не нужна |
| Поддержка браузеров | IE10+ | Все браузеры |
| Аналитика | Нативная | Требует настройки |
Nginx fallback для History API
# nginx.conf
location / {
try_files $uri $uri/ /index.html;
}
Частые ошибки
- Не настроен server fallback для History API — при прямом заходе на
/aboutсервер возвращает 404, потому что файлаaboutнет; нужно настроить редирект наindex.html. - Используют HashRouter для SEO-важных страниц — поисковики плохо индексируют hash-URL.
- Не обрабатывают
popstate— кнопки «Назад»/«Вперёд» не работают в самописном роутере.