Интеграция с серверным хранилищем

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


Сохранение состояния пользователя

Для того чтобы пользователь мог продолжить обучение с того места, где он остановился, необходимо сохранять текущий шаг на сервере. Для этого используется AJAX-запрос к серверному API. Пример:

function saveUserProgress(userId, step) {
    fetch('/api/saveProgress', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ userId, step })
    })
    .then(response => response.json())
    .then(data => {
        console.log('Прогресс сохранён:', data);
    })
    .catch(error => console.error('Ошибка при сохранении прогресса:', error));
}

В этом примере сервер получает userId и текущий шаг step и сохраняет их в базе данных. На стороне клиента можно использовать эту функцию в событии onchange:

introJs().onchange(function(targetElement) {
    const step = this._currentStep;
    saveUserProgress(currentUserId, step);
});

Загрузка последовательности шагов с сервера

Часто шаги руководства динамически формируются в зависимости от данных пользователя. Для этого список шагов можно получать с сервера в формате JSON:

fetch(`/api/getIntroSteps?userId=${currentUserId}`)
    .then(res => res.json())
    .then(steps => {
        introJs().setOptions({ steps }).start();
    });

Пример структуры данных для шагов:

[
    {
        "element": "#button1",
        "intro": "Это основная кнопка для действий",
        "position": "right"
    },
    {
        "element": "#menu",
        "intro": "Здесь находится главное меню",
        "position": "bottom"
    }
]

Использование серверного API позволяет создавать персонализированные инструкции, например скрывать шаги, которые пользователь уже выполнил.


Аутентификация и безопасность

При работе с серверным хранилищем необходимо учитывать безопасность:

  • Токены аутентификации: передавать JWT или сессионный идентификатор вместе с запросом.
  • Проверка прав доступа: сервер должен проверять, что пользователь может получить или сохранять данные именно этого руководства.
  • Шифрование данных: при необходимости чувствительные данные можно передавать через HTTPS и хранить в зашифрованном виде.

Пример передачи токена:

fetch('/api/saveProgress', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${userToken}`
    },
    body: JSON.stringify({ userId, step })
});

Динамическое изменение шагов

С помощью сервера можно динамически менять порядок шагов в зависимости от действий пользователя или версии интерфейса. Например, если пользователь пропустил определённые функции, сервер может добавить дополнительные шаги:

fetch(`/api/getDynamicSteps?userId=${currentUserId}`)
    .then(res => res.json())
    .then(steps => {
        const intro = introJs();
        intro.setOptions({ steps });
        intro.start();
    });

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


События Intro.js для серверной интеграции

Intro.js предоставляет несколько событий, которые удобно использовать для синхронизации с сервером:

  • onchange(targetElement) — вызывается при смене шага, можно сохранять прогресс.
  • onexit() — вызывается при завершении руководства, можно отправить финальный статус.
  • onbeforechange(targetElement) — позволяет предотвратить переход к следующему шагу, например, если сервер возвращает ошибку.

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

introJs().onexit(function() {
    fetch('/api/finishIntro', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ userId: currentUserId })
    });
});

Кэширование и оптимизация

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

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

Пример:

function saveProgressLocally(step) {
    localStorage.setItem('introStep', step);
}

function syncProgressWithServer(userId) {
    const step = localStorage.getItem('introStep');
    if (step) {
        saveUserProgress(userId, step);
    }
}

Обработка ошибок и повторные попытки

При интеграции с сервером необходимо учитывать ошибки сети и возможность повторных попыток:

async function saveProgressWithRetry(userId, step, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            await fetch('/api/saveProgress', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ userId, step })
            });
            return;
        } catch (e) {
            if (i === retries - 1) console.error('Не удалось сохранить прогресс:', e);
        }
    }
}

Интеграция с базой данных

На серверной стороне прогресс и шаги обычно хранятся в базе данных. Типичная структура таблиц:

Таблица intro_steps:

id element intro position

Таблица user_intro_progress:

user_id | current_step | last_updated |

Такое разделение позволяет:

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