Загрузка и обработка ошибок
Состояние ошибки и слот fallback
Fallback по умолчанию — если слот #error не задан, показывается серый прямоугольник с SVG-иконкой битого изображения:
<VImage src="/missing.jpg" alt="Missing" :width="400" :height="300" />
<!-- Показывает: серый прямоугольник + SVG-иконка -->Кастомный fallback через слот:
<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:
<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" — ради полного контроля над тем, когда начинается загрузка.
<!-- По умолчанию: загружается, когда изображение в 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" />Как это работает
- При монтировании — создаётся
IntersectionObserverи начинает наблюдать за оборачивающим элементом - Когда элемент попадает во вьюпорт (с учётом
rootMargin) — устанавливаетсяsrcизображения, и начинается загрузка (status: 'loading') - Когда изображение загружается —
statusпереходит в'loaded'; плейсхолдер исчезает - Observer отключается после первого пересечения — никаких лишних колбэков
Поведение при SSR
На сервере IntersectionObserver недоступен. <VImage> рендерит обычный <img loading="lazy"> без какого-либо JS-управляемого состояния. После гидратации onMounted настраивает IO как обычно.
fetchpriority и decoding
Управление приоритизацией браузера и стратегией декодирования:
<!-- 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' | Режим декодирования изображения |
Повтор при ошибке
Автоматически повторяет неудачные загрузки изображений с экспоненциальной задержкой:
<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, ничего не ломается, просто не может помочь.
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.
<VImage
src="/photo.jpg"
alt="Photo"
priority
respect-save-data
:densities="{ 1: '/photo.jpg', 2: '/photo@2x.jpg' }"
/>Для прямой проверки вне компонента (например, перед запуском пакетной предзагрузки вручную) isSaveDataEnabled() — та же проверка без реактивной обёртки:
import { isSaveDataEnabled } from 'vue-image-kit'
if (!isSaveDataEnabled()) {
await preload(nextSlideUrls)
}useImagePreloader
Предзагрузка пакета изображений перед навигацией — полезно для галерей и каруселей.
<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>