Skip to content

Загрузка и обработка ошибок ​

Состояние ошибки и слот fallback ​

Fallback по умолчанию — если слот #error не задан, показывается серый прямоугольник с SVG-иконкой битого изображения:

vue
<VImage src="/missing.jpg" alt="Missing" :width="400" :height="300" />
<!-- Показывает: серый прямоугольник + SVG-иконка -->

Кастомный fallback через слот:

vue
<VImage src="/missing.jpg" alt="Missing" :width="400" :height="300">
  <template #error>
    <div class="error-placeholder">
      <img src="/no-image.svg" alt="" />
      <p>Image is currently unavailable</p>
    </div>
  </template>
</VImage>

Обработка ошибок в JavaScript:

vue
<script setup lang="ts">
function handleError(e: Event) {
  console.error('Image failed to load:', e)
  // Отправить в Sentry, переключиться на резервный URL и т. д.
}
</script>

<template>
  <VImage src="/photo.jpg" alt="Photo" @error="handleError" />
</template>

Ленивая загрузка ​

<VImage> использует IntersectionObserver для ленивой загрузки — а не нативный атрибут loading="lazy" — ради полного контроля над тем, когда начинается загрузка. Внутри использует useLazyLoad(), доступный отдельно для headless-сценариев.

vue
<!-- По умолчанию: загружается, когда изображение в 200px от вьюпорта -->
<VImage src="/photo.jpg" alt="Photo" />

<!-- Кастомный rootMargin — начать загрузку за 500px до вьюпорта -->
<VImage src="/photo.jpg" alt="Photo" root-margin="500px" />

<!-- Загрузка, когда видно 50% изображения -->
<VImage src="/photo.jpg" alt="Photo" :threshold="0.5" />

<!-- Отключить ленивую загрузку — грузить немедленно (над первым экраном) -->
<VImage src="/photo.jpg" alt="Photo" :lazy="false" />

Как это работает ​

  1. При монтировании — создаётся IntersectionObserver и начинает наблюдать за элементом-заглушкой (см. Компоновка без обёртки — постоянной обёртки нет, есть только этот один элемент до начала загрузки)
  2. Когда элемент попадает во вьюпорт (с учётом rootMargin) — устанавливается src изображения, начинается загрузка (status: 'loading'), и элемент-заглушка заменяется настоящим <img>/<picture>
  3. Когда изображение загружается — status переходит в 'loaded'; фон-плейсхолдер перекрывается отрисованным фото — мгновенно по умолчанию (опциональный проп fadeIn вместо этого плавно проявляет сам блок)
  4. Observer отключается после первого пересечения — никаких лишних колбэков

Поведение при SSR ​

На сервере IntersectionObserver недоступен. <VImage> рендерит обычный <img loading="lazy"> без какого-либо JS-управляемого состояния. После гидратации onMounted настраивает IO как обычно.

fetchpriority и decoding ​

Управление приоритизацией браузера и стратегией декодирования:

vue
<!-- Hero-изображение: грузить первым, декодировать асинхронно -->
<VImage src="/hero.jpg" alt="Hero" :lazy="false" fetchpriority="high" decoding="async" />

<!-- Ниже первого экрана: понизить приоритет -->
<VImage src="/footer-banner.jpg" alt="Banner" fetchpriority="low" />
  • fetchpriority — 'high' | 'low' | 'auto', опционален. Подсказка браузеру о приоритете загрузки.
  • decoding — 'async' | 'sync' | 'auto' · по умолчанию: 'async'. Режим декодирования изображения.

Повтор при ошибке ​

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

vue
<VImage src="/flaky-image.jpg" alt="Photo" :max-retries="3" :retry-delay="500" />
  • maxRetries — number · по умолчанию: 0. Максимум попыток повтора.
  • retryDelay — number · по умолчанию: 1000. Начальная задержка в мс (удваивается на каждом повторе).

Адаптация к сети ​

useNetworkAware() оборачивает Network Information API браузера — saveData (пользователь включил экономию трафика) и effectiveType ('slow-2g' | '2g' | '3g' | '4g'). SSR-безопасен (saveData на сервере начинается с false) и реактивен к событию change соединения. Поддержка сегодня только в Chromium (Firefox/Safari API не реализуют) — saveData там просто остаётся false, ничего не ломается, просто не может помочь.

ts
import { useNetworkAware } from '@macrulez/vue-image-kit'

const { saveData, effectiveType } = useNetworkAware()

Уже используется в двух местах:

  • useImagePreloader() молча пропускает вызовы preload(), пока включён saveData — предзагрузка обменивает трафик на более плавный переход позже, а это неверный компромисс, раз пользователь попросил экономить трафик.
  • Проп respectSaveData компонента VImage (опционален, по умолчанию false): пока saveData включён, он нейтрализует priority (изображение остаётся lazy вместо форсированного eager/высокого приоритета) и понижает src до наименьшего URL, который реально можно найти — наименьший ключ в карте densities или наименьший кандидат w в image.srcset (значение манифеста/?vik). У обычного widths нет отдельного URL, до которого можно понизиться (см. generateSrcset — браузер договаривается через дескриптор w против одного URL, а не URL на каждую ширину), поэтому там это no-op.
vue
<VImage
  src="/photo.jpg"
  alt="Photo"
  priority
  respect-save-data
  :densities="{ 1: '/photo.jpg', 2: '/photo@2x.jpg' }"
/>

Для прямой проверки вне компонента (например, перед запуском пакетной предзагрузки вручную) isSaveDataEnabled() — та же проверка без реактивной обёртки:

ts
import { isSaveDataEnabled } from '@macrulez/vue-image-kit'

if (!isSaveDataEnabled()) {
  await preload(nextSlideUrls)
}