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" — ради полного контроля над тем, когда начинается загрузка.

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')
  3. Когда изображение загружается — status переходит в 'loaded'; плейсхолдер исчезает
  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" />
ПропТипПо умолчаниюОписание
maxRetriesnumber0Максимум попыток повтора
retryDelaynumber1000Начальная задержка в мс (удваивается на каждом повторе)

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

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 '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 'vue-image-kit'

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

useImagePreloader

Предзагрузка пакета изображений перед навигацией — полезно для галерей и каруселей.

vue
<script setup lang="ts">
import { useImagePreloader } from 'vue-image-kit'

const { preload, progress, isComplete, errors } = useImagePreloader()

async function goToNextSlide() {
  await preload(['/slide-2.jpg', '/slide-3.jpg'])
  // Все изображения закэшированы — переход мгновенный
  currentSlide.value++
}
</script>

<template>
  <div v-if="!isComplete">Loading {{ progress }}%…</div>
</template>