Синхронизация с внешним хранилищем

В контексте Universal Router синхронизация с внешним хранилищем — это процесс, при котором маршрутизатор взаимодействует с внешними источниками данных для получения или сохранения состояния маршрутов. Внешние хранилища могут включать локальное хранилище браузера (localStorage, sessionStorage), IndexedDB, серверные API или Redux-стор. Основная цель — обеспечить постоянство состояния навигации и возможность восстановления маршрутов после перезагрузки страницы или перехода между вкладками.

Ключевые задачи синхронизации:

  • Хранение текущего маршрута для восстановления состояния.
  • Передача динамических параметров маршрута внешним сервисам.
  • Обеспечение совместимости маршрутов с историей браузера.
  • Реактивное обновление маршрутов при изменении данных во внешнем хранилище.

Хранение состояния маршрута

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

Пример использования localStorage:

import UniversalRouter from 'universal-router';

const routes = [
  {
    path: '/',
    action: () => 'Главная страница',
  },
  {
    path: '/profile/:id',
    action: ({ params }) => `Профиль пользователя ${params.id}`,
  }
];

const router = new UniversalRouter(routes);

async function navigate(path) {
  const result = await router.resolve(path);
  // Сохраняем последний путь
  localStorage.setItem('lastRoute', path);
  return result;
}

// Восстановление маршрута при загрузке страницы
const lastRoute = localStorage.getItem('lastRoute');
if (lastRoute) {
  router.resolve(lastRoute).then(page => {
    console.log(page);
  });
}

Ключевой момент: синхронизация состояния маршрута позволяет пользователю вернуться к последней посещенной странице без дополнительной логики на сервере.

Реактивная синхронизация с Redux или Vuex

При использовании глобальных сторов (Redux, Vuex) можно реализовать реактивное обновление маршрутов на основе состояния приложения. В этом случае Universal Router не только читает состояние, но и инициирует изменения при навигации.

Пример интеграции с Redux:

import { createStore } from 'redux';
import UniversalRouter from 'universal-router';

const initialState = {
  currentRoute: '/',
};

function reducer(state = initialState, action) {
  switch(action.type) {
    case 'NAVIGATE':
      return { ...state, currentRoute: action.path };
    default:
      return state;
  }
}

const store = createStore(reducer);

const routes = [
  { path: '/', action: () => 'Главная' },
  { path: '/about', action: () => 'О нас' },
];

const router = new UniversalRouter(routes);

store.subscribe(() => {
  const state = store.getState();
  router.resolve(state.currentRoute).then(page => console.log(page));
});

function navigate(path) {
  store.dispatch({ type: 'NAVIGATE', path });
}

Вывод: синхронизация с глобальным стором позволяет централизованно управлять навигацией и отслеживать изменения маршрутов в реальном времени.

Асинхронная синхронизация с сервером

Иногда необходимо сохранять состояние маршрута на сервере, чтобы обеспечить мультиустройственный доступ. В этом случае Universal Router интегрируется с асинхронными API-запросами, при этом разрешение маршрута может зависеть от данных сервера.

Пример:

import UniversalRouter from 'universal-router';

const routes = [
  {
    path: '/dashboard',
    async action() {
      const userData = await fetch('/api/user-data').then(res => res.json());
      return `Дашборд пользователя ${userData.name}`;
    }
  }
];

const router = new UniversalRouter(routes);

async function navigate(path) {
  const page = await router.resolve(path);
  // Отправляем текущий маршрут на сервер
  await fetch('/api/save-route', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ path })
  });
  return page;
}

Особенности:

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

События и обратные вызовы для синхронизации

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

Пример:

const subscribers = [];

function subscribe(callback) {
  subscribers.push(callback);
}

function notify(path) {
  subscribers.forEach(cb => cb(path));
}

async function navigate(path) {
  const page = await router.resolve(path);
  notify(path);
  return page;
}

// Использование
subscribe(path => {
  localStorage.setItem('currentRoute', path);
});

Преимущество: такой подход обеспечивает гибкость в интеграции с любыми внешними системами и позволяет управлять синхронизацией независимо от конкретного хранилища.

Поддержка истории браузера

Для синхронизации маршрутов с history API необходимо учитывать:

  • pushState для создания нового состояния без перезагрузки.
  • replaceState для обновления текущего состояния.
  • Обработку события popstate для реакции на кнопки «назад» и «вперед».

Пример интеграции:

window.addEventListener('popstate', () => {
  router.resolve(window.location.pathname);
});

function navigate(path) {
  history.pushState({}, '', path);
  router.resolve(path);
}

Ключевой момент: синхронизация с историей браузера позволяет сохранить привычное поведение навигации, одновременно обеспечивая реактивность Universal Router.


Эти подходы формируют полноценную стратегию синхронизации Universal Router с внешними хранилищами, обеспечивая сохранность маршрутов, поддержку динамических данных и реактивность навигации в сложных приложениях.