Реактивность состояния

Navigo — это лёгкая библиотека маршрутизации для JavaScript, обеспечивающая управление URL и реактивное изменение состояния приложения без перезагрузки страницы. Она работает по принципу клиентской маршрутизации, что позволяет создавать одностраничные приложения (SPA) с динамическим изменением контента.

Для инициализации маршрутизатора используется конструктор:

import Navigo from 'navigo';

const router = new Navigo('/', { hash: true });

Параметр '/' указывает базовый путь приложения, а { hash: true } активирует использование хешей в URL для поддержки старых браузеров.

Определение маршрутов

Маршруты задаются методом on. Каждому маршруту соответствует функция-обработчик:

router
  .on('/home', () => {
    renderHomePage();
  })
  .on('/about', () => {
    renderAboutPage();
  })
  .resolve();

resolve() запускает проверку текущего URL и активирует соответствующий обработчик.

Маршруты поддерживают параметры и динамические сегменты:

router.on('/user/:id', (params) => {
  console.log(params.id); // доступ к значению параметра
});

Реактивность состояния

Одним из ключевых аспектов SPA является реактивное управление состоянием, когда изменение данных автоматически обновляет отображение без полной перезагрузки страницы. В контексте Navigo реактивность реализуется через подписку на изменения маршрута и связывание их с состоянием приложения.

Использование объекта состояния

const state = {
  currentPage: null,
  userId: null,
};

function setState(newState) {
  Object.assign(state, newState);
  updateView(state);
}

function updateView(state) {
  switch (state.currentPage) {
    case 'home':
      renderHomePage();
      break;
    case 'about':
      renderAboutPage();
      break;
    case 'user':
      renderUserPage(state.userId);
      break;
  }
}

Связывание маршрутов с состоянием

Маршрутизатор обновляет состояние при изменении URL:

router
  .on('/home', () => {
    setState({ currentPage: 'home', userId: null });
  })
  .on('/about', () => {
    setState({ currentPage: 'about', userId: null });
  })
  .on('/user/:id', (params) => {
    setState({ currentPage: 'user', userId: params.id });
  })
  .resolve();

В этой схеме Navigo выступает посредником, который преобразует URL в реактивное состояние, а функция setState синхронизирует отображение с этим состоянием.

Поддержка асинхронного состояния

В современных приложениях данные часто загружаются асинхронно с сервера. Реактивность требует обработки промисов:

router.on('/user/:id', async (params) => {
  setState({ currentPage: 'user', userId: params.id });
  const userData = await fetchUserData(params.id);
  setState({ userData });
});

async function fetchUserData(id) {
  const response = await fetch(`/api/users/${id}`);
  return await response.json();
}

В этом примере маршрут инициирует загрузку данных, а изменение состояния userData автоматически обновляет представление.

Реактивные переходы

Навигация по приложению без перезагрузки реализуется через методы маршрутизатора:

router.navigate('/home');  // программная смена маршрута
router.updatePageLinks();   // обновление ссылок для правильного управления маршрутизацией

Метод updatePageLinks позволяет использовать обычные <a> ссылки с атрибутом href, превращая их в реактивные переходы без перезагрузки страницы.

Поддержка wildcard и fallback

Для обработки неизвестных маршрутов используется универсальный обработчик:

router.notFound(() => {
  setState({ currentPage: 'notfound' });
  renderNotFoundPage();
});

Такой подход гарантирует, что всегда поддерживается реактивное состояние, даже если URL не соответствует ни одному маршруту.

Вложенные маршруты и состояние

Navigo поддерживает вложенные маршруты, что позволяет структурировать приложение и управлять сложными состояниями:

router
  .on('/dashboard', () => setState({ currentPage: 'dashboard' }))
  .on('/dashboard/settings', () => setState({ currentPage: 'settings' }))
  .on('/dashboard/profile', () => setState({ currentPage: 'profile' }))
  .resolve();

В этом случае состояние currentPage отражает текущий подмаршрут, позволяя компонентам корректно реагировать на изменение URL.

Принципы реактивного управления

  • Единая точка состояния: все данные, влияющие на отображение, должны храниться в едином объекте состояния.
  • Чистые функции отображения: renderXPage должны быть детерминированы только состоянием.
  • Синхронизация URL и состояния: Navigo обеспечивает прямую связь между URL и состоянием приложения.
  • Асинхронная загрузка данных: состояние обновляется только после получения данных, что сохраняет реактивность интерфейса.
  • Fallback и ошибки: маршруты с неизвестным URL не разрушают реактивность, а инициируют отдельное состояние ошибки.

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

Navigo можно интегрировать с любыми фреймворками, включая React, Vue и Svelte, через обёртку состояния:

import { writable } from 'svelte/store';

export const appState = writable({ currentPage: 'home' });

router.on('/about', () => appState.set({ currentPage: 'about' }));
router.on('/user/:id', (params) => appState.set({ currentPage: 'user', userId: params.id }));

С помощью реактивных хранилищ можно автоматически обновлять компоненты при изменении маршрута, сохраняя архитектуру SPA чистой и предсказуемой.

Обработка событий перехода

Navigo поддерживает события on и resolve, позволяя подписываться на переходы и выполнять побочные эффекты:

router.hooks({
  before: (done, params) => {
    console.log('Переход начинается', params);
    done();
  },
  after: (params) => {
    console.log('Переход завершён', params);
  }
});

Такой механизм полезен для анимаций, логирования или условной проверки доступа, сохраняя реактивность интерфейса.

Итоговая структура реактивного приложения

  1. Создание экземпляра Navigo и базовых маршрутов.
  2. Определение объекта состояния с ключевыми данными.
  3. Функция setState для изменения состояния и вызова рендеринга.
  4. Связывание маршрутов с обновлением состояния.
  5. Асинхронная загрузка данных и обработка промисов.
  6. Поддержка вложенных маршрутов и fallback.
  7. Интеграция с реактивными хранилищами для сложных SPA.

Эта архитектура позволяет строить SPA с полной реактивностью, где каждый переход по URL мгновенно синхронизируется с состоянием приложения и интерфейсом.