Индикаторы загрузки

Navigo — это легковесная библиотека маршрутизации для JavaScript, обеспечивающая работу с клиентскими маршрутами и управление переходами между состояниями приложения. При работе с динамическим контентом, который загружается асинхронно, крайне важно обеспечивать пользователю визуальную обратную связь о процессе загрузки. Для этого используются индикаторы загрузки (loading indicators), которые информируют о том, что данные находятся в процессе получения или обработки.


Создание базового индикатора загрузки

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

const loader = document.createElement('div');
loader.id = 'loader';
loader.textContent = 'Загрузка...';
loader.style.display = 'none';
document.body.appendChild(loader);

function showLoader() {
  loader.style.display = 'block';
}

function hideLoader() {
  loader.style.display = 'none';
}

Здесь showLoader() и hideLoader() управляют видимостью индикатора. В реальных приложениях можно использовать CSS-анимации, спиннеры или прогресс-бары для улучшения визуального эффекта.


Интеграция индикатора с Navigo

Navigo позволяет назначать обработчики для маршрутов через метод on(). Для интеграции индикатора загрузки необходимо оборачивать асинхронные функции, которые подгружают данные, в вызовы showLoader() и hideLoader(). Пример:

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

router.on('/users', async () => {
  showLoader();
  try {
    const response = await fetch('/api/users');
    const users = await response.json();
    renderUsers(users);
  } catch (error) {
    console.error(error);
    renderError(error);
  } finally {
    hideLoader();
  }
});

router.resolve();

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


Работа с динамическими маршрутами и параметрами

Для маршрутов с параметрами важно корректно управлять индикатором при каждой загрузке. Например:

router.on('/users/:id', async (params) => {
  showLoader();
  try {
    const response = await fetch(`/api/users/${params.id}`);
    const user = await response.json();
    renderUserProfile(user);
  } catch (error) {
    renderError(error);
  } finally {
    hideLoader();
  }
});

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


Глобальный индикатор для всех маршрутов

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

router.hooks({
  before: (done, match) => {
    showLoader();
    done();
  },
  after: () => {
    hideLoader();
  }
});

Такой подход избавляет от необходимости дублировать вызовы showLoader() и hideLoader() в каждом обработчике маршрута.


Асинхронные загрузки с параллельными запросами

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

router.on('/dashboard', async () => {
  showLoader();
  try {
    const [users, stats] = await Promise.all([
      fetch('/api/users').then(res => res.json()),
      fetch('/api/stats').then(res => res.json())
    ]);
    renderDashboard(users, stats);
  } catch (error) {
    renderError(error);
  } finally {
    hideLoader();
  }
});

Использование Promise.all позволяет контролировать индикатор до завершения всех необходимых запросов, предотвращая преждевременное скрытие.


Совмещение с визуальными эффектами

Для улучшения UX индикатор можно совмещать с CSS-анимациями или библиотеками спиннеров. Например:

#loader {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-weight: bold;
  background: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 1em 2em;
  border-radius: 8px;
  display: none;
}

Добавление плавного появления и исчезновения через opacity или transition делает интерфейс более отзывчивым и приятным визуально.


Контроль частоты отображения

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

let loaderTimeout;

function showLoaderDelayed() {
  loaderTimeout = setTimeout(showLoader, 100);
}

function hideLoaderImmediate() {
  clearTimeout(loaderTimeout);
  hideLoader();
}

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


Работа с отменой загрузки

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

router.on('/users', async () => {
  const controller = new AbortController();
  const signal = controller.signal;
  showLoader();
  try {
    const response = await fetch('/api/users', { signal });
    const users = await response.json();
    renderUsers(users);
  } catch (error) {
    if (error.name !== 'AbortError') renderError(error);
  } finally {
    hideLoader();
  }
  
  return () => controller.abort();
});

Метод возвращает функцию отмены, которая вызывается Navigo при переходе на другой маршрут, предотвращая отображение устаревших данных и корректно скрывая индикатор.


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