История браузера и back/forward навигация

Основы истории браузера

В браузерах современного типа хранится история навигации, которая позволяет пользователю перемещаться назад и вперёд между посещёнными страницами. История представлена в виде стэка, где каждая страница добавляется при загрузке нового URL. Основное взаимодействие с историей осуществляется через объект history, доступный в глобальном контексте window.

console.log(window.history.length); // количество записей в истории

Объект history предоставляет методы для управления навигацией:

  • back() — возвращает пользователя на предыдущую страницу в истории;
  • forward() — перемещает вперёд по истории, если было возвращение назад;
  • go(n) — перемещается на n шагов в истории (отрицательные значения — назад, положительные — вперёд).
window.history.back(); // аналог кнопки "назад"
window.history.forward(); // аналог кнопки "вперед"
window.history.go(-2); // перемещение на две страницы назад

Структура истории и стейт

Каждая запись в истории может содержать состояние (state), которое сохраняется между навигациями. Состояние позволяет хранить данные без изменения URL или необходимости использовать сервер. Методы работы со стейтом:

  • pushState(stateObj, title, url) — добавляет новую запись в историю с заданным состоянием и URL;
  • replaceState(stateObj, title, url) — заменяет текущую запись, не создавая новую;
window.history.pushState({page: 1}, "Страница 1", "/page1");
window.history.replaceState({page: 2}, "Страница 2", "/page2");

Использование state особенно актуально в одностраничных приложениях (SPA), где переходы между состояниями не требуют полной перезагрузки страницы.

Событие popstate

Навигация назад и вперёд вызывает событие popstate, которое позволяет реагировать на изменения истории без перезагрузки страницы:

window.addEventListener('popstate', (event) => {
    console.log('Текущее состояние:', event.state);
});

При переходе по истории объект event.state содержит состояние, ранее сохранённое через pushState или replaceState. Если состояние отсутствует, event.state будет null.

Интеграция с фреймворком Riot.js

Riot.js предоставляет удобный механизм для управления состоянием интерфейса и переходами между компонентами. В SPA на Riot.js навигация через history интегрируется с компонентами следующим образом:

  1. Отслеживание изменений истории через popstate.
  2. Обновление состояния компонентов в зависимости от URL и state.
  3. Динамическое рендерирование компонентов без перезагрузки страницы.

Пример обработки навигации в Riot.js:

import { component, mount } from 'riot';

component('app', {
  state: { page: 'home' },

  onBeforeMount() {
    window.addEventListener('popstate', this.handlePopState.bind(this));
  },

  handlePopState(event) {
    this.update({ page: event.state?.page || 'home' });
  },

  navigate(page) {
    this.update({ page });
    window.history.pushState({ page }, '', `/${page}`);
  }
});

mount('app', document.body);

В этом примере при вызове navigate состояние обновляется и записывается в историю, а возврат назад через браузер автоматически восстанавливает состояние компонента.

Особенности управления историей

  • Ограничения по длине стека: большинство браузеров ограничивают размер истории, удаляя старые записи при превышении лимита.
  • Смешение pushState и replaceState: pushState создаёт новую запись, replaceState заменяет существующую. Это важно при обновлении текущей страницы без увеличения стека.
  • Асинхронная загрузка контента: в SPA переходы часто сопровождаются подгрузкой данных через AJAX. Сохранение состояния в истории позволяет пользователю возвращаться к предыдущим представлениям без повторной загрузки.

Взаимодействие с URL

Методы pushState и replaceState позволяют изменять URL без перезагрузки страницы, что критично для SEO и прямых ссылок:

window.history.pushState({section: 'about'}, 'О нас', '/about');

Браузер обновляет адресную строку, но DOM остаётся прежним, а состояние доступно через history.state.

Практическая рекомендация

Для полноценной SPA-архитектуры на Riot.js рекомендуется:

  1. Использовать pushState для всех переходов между логическими страницами.
  2. Реализовать обработку popstate для синхронизации состояния компонентов с историей.
  3. Хранить минимальный объём данных в state — только то, что необходимо для восстановления интерфейса.
  4. Поддерживать соответствие URL и состояния, чтобы пользователь мог делиться ссылками и возвращаться к ним напрямую.

Итоговый механизм

Комбинация history и событий popstate позволяет реализовать:

  • Управляемую навигацию без перезагрузки страницы;
  • Сохранение состояния компонентов между переходами;
  • Поддержку кнопок браузера «назад» и «вперед» в SPA;
  • Изменение URL без потери данных и без отправки новых запросов на сервер.

Эти возможности делают историю браузера и методы pushState/replaceState центральным инструментом для построения современных интерфейсов на Riot.js.