Персистентность состояния

Shepherd.js — это библиотека для создания интерактивных пользовательских туров на веб-страницах. Одной из важных задач при работе с турами является сохранение прогресса пользователя и возможность продолжить тур после перезагрузки страницы или закрытия браузера. Этот механизм называется персистентностью состояния.


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

Shepherd.js не предоставляет встроенного механизма для хранения состояния между сессиями, поэтому разработчики используют внешние хранилища:

  • LocalStorage — простой способ сохранить состояние на стороне клиента.
  • SessionStorage — временное хранилище, которое очищается после закрытия вкладки.
  • Cookies — могут использоваться для коротких флагов, совместимы с серверной логикой.
  • Backend — для более сложных сценариев, когда требуется синхронизация между устройствами.

Пример использования LocalStorage для сохранения шага текущего тура:

const tour = new Shepherd.Tour({
  defaultStepOptions: {
    scrollTo: true,
    cancelIcon: { enabled: true }
  }
});

tour.addStep({
  id: 'step-1',
  text: 'Это первый шаг',
  attachTo: { element: '#step1', on: 'bottom' },
  buttons: [
    {
      text: 'Далее',
      action: () => {
        localStorage.setItem('tourStep', 'step-2');
        tour.next();
      }
    }
  ]
});

tour.addStep({
  id: 'step-2',
  text: 'Второй шаг тура',
  attachTo: { element: '#step2', on: 'top' },
  buttons: [
    {
      text: 'Назад',
      action: () => {
        localStorage.setItem('tourStep', 'step-1');
        tour.back();
      }
    }
  ]
});

// Восстановление состояния при загрузке страницы
const savedStep = localStorage.getItem('tourStep');
if (savedStep) {
  tour.start();
  tour.show(savedStep);
} else {
  tour.start();
}

Управление прогрессом пользователя

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

  1. Уникальные идентификаторы шагов — каждый шаг должен иметь уникальное id, чтобы можно было точно определить, на каком этапе остановился пользователь.
  2. События Shepherd.js — библиотека генерирует события, которые можно использовать для обновления состояния:
tour.on('show', (event) => {
  localStorage.setItem('tourStep', event.step.id);
});

tour.on('complete', () => {
  localStorage.removeItem('tourStep');
});
  1. Обработка закрытия и отмены тура — если пользователь отменяет тур или закрывает модальное окно, состояние должно корректно очищаться или сохраняться в зависимости от логики приложения.

Продвинутая персистентность

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

const tourState = {
  currentStep: 'step-1',
  completedSteps: [],
  dismissed: false
};

tour.on('show', (event) => {
  tourState.currentStep = event.step.id;
  if (!tourState.completedSteps.includes(event.step.id)) {
    tourState.completedSteps.push(event.step.id);
  }
  localStorage.setItem('tourState', JSON.stringify(tourState));
});

tour.on('complete', () => {
  tourState.dismissed = true;
  localStorage.setItem('tourState', JSON.stringify(tourState));
});

Такой подход позволяет:

  • Пропускать уже пройденные шаги при повторном запуске тура.
  • Управлять частичной завершённостью и восстановлением прогресса.
  • Поддерживать разные ветвления туров в зависимости от действий пользователя.

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

Если приложение требует синхронизации прогресса между устройствами, состояние тура можно отправлять на сервер через API:

fetch('/api/tour-state', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(tourState)
});

При загрузке страницы сервер может вернуть состояние пользователя, чтобы возобновить тур:

fetch('/api/tour-state')
  .then(res => res.json())
  .then(data => {
    if (data && !data.dismissed) {
      tour.start();
      tour.show(data.currentStep);
    }
  });

Рекомендации по надёжной персистентности

  • Всегда проверять существование сохранённого шага перед запуском метода show, чтобы избежать ошибок при удалении элементов DOM.
  • Сохранять не только идентификатор шага, но и состояние элементов, если шаги зависят от динамически создаваемого контента.
  • Использовать tour.on('cancel', ...) для очистки состояния при явном закрытии тура пользователем.
  • Для SPA-приложений учитывать смену маршрутов, чтобы корректно продолжать тур после навигации.

Персистентность состояния в Shepherd.js позволяет создавать гибкие, надёжные туры, которые учитывают действия пользователя, обеспечивают удобство возвращения к интерфейсу и интеграцию с клиентским и серверным хранилищем.