Composables, директива и плагин
useImage
Headless composable. Используйте, когда нужен конечный автомат состояния загрузки и вычисляемые атрибуты, но требуется своя разметка.
const {
status, // Ref<'idle' | 'loading' | 'loaded' | 'error'>
isLoaded, // ComputedRef<boolean>
isError, // ComputedRef<boolean>
imgAttrs, // ComputedRef<ImgAttrs> — готов для spread на <img>
observe, // (el: Ref<HTMLElement | null>) => void
onImgLoad, // () => void — вызывать из img @load
onImgError, // () => void — вызывать из img @error
} = useImage(options)Опции
| Опция | Тип | По умолчанию | Описание |
|---|---|---|---|
src | string | SrcSet | — | URL изображения или объект формата |
widths | number[] | [] | Ширины для генерации srcset на основе ширины (w) |
densities | number[] | Record<number, string> | — | Дескрипторы плотности (1x/2x/3x); список переиспользует src, объект даёт отдельные файлы; имеет приоритет над widths, игнорирует sizes |
sizes | string | — | Значение атрибута sizes (только для srcset на основе ширины) |
lazy | boolean | true | Включить IntersectionObserver |
rootMargin | string | "200px" | rootMargin для IO |
threshold | number | 0 | threshold для IO |
fit | ObjectFit | "cover" | Стиль object-fit |
maxRetries | number | 0 | Максимум попыток повтора при неудаче загрузки |
retryDelay | number | 1000 | Начальная задержка в мс; удваивается на каждом повторе |
Конечный автомат
idle → loading → loaded
→ error- При
lazy: true— переход вloading, когда наблюдаемый элемент попадает во вьюпорт - При
lazy: false— переход вloadingсразу послеonMounted
Возвращаемое значение
| Свойство | Тип | Описание |
|---|---|---|
status | Ref<ImageStatus> | Текущее состояние загрузки |
isLoaded | ComputedRef<boolean> | true, когда status === 'loaded' |
isError | ComputedRef<boolean> | true, когда status === 'error' |
imgAttrs | ComputedRef<object> | { src, srcset?, sizes?, style } — готов для v-bind |
observe | Function | Передайте Ref<HTMLElement>, чтобы начать отслеживание пересечения |
onImgLoad | Function | Вызывайте из <img @load> для перехода в loaded |
onImgError | Function | Вызывайте из <img @error> для перехода в error |
Пример — кастомный рендер
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useImage } from 'vue-image-kit'
const containerRef = ref<HTMLElement | null>(null)
const { status, isLoaded, imgAttrs, observe, onImgLoad, onImgError } = useImage({
src: '/photo.jpg',
widths: [400, 800, 1200],
sizes: '(max-width: 768px) 100vw, 50vw',
})
onMounted(() => {
observe(containerRef)
})
</script>
<template>
<div ref="containerRef" class="image-wrapper">
<div v-if="status === 'idle'" class="skeleton" />
<img
v-if="status === 'loading' || isLoaded"
v-bind="imgAttrs"
alt="Photo"
:class="{ visible: isLoaded }"
@load="onImgLoad"
@error="onImgError"
/>
<div v-if="status === 'error'" class="error-state">Failed to load</div>
</div>
</template>
<style scoped>
img {
opacity: 0;
transition: opacity 0.3s;
}
img.visible {
opacity: 1;
}
</style>vLazyImg
Директива для установки background-image на любой элемент после его появления во вьюпорте. Используйте, когда нельзя применить компонент <VImage> — CSS-фоны, обёртки сторонних библиотек и т. д.
<!-- Простая строка -->
<div v-lazy-img="'/background.jpg'" class="hero" />
<!-- Объект с опциями -->
<div
v-lazy-img="{
src: '/background.jpg',
placeholder: 'data:image/jpeg;base64,...',
rootMargin: '100px',
onLoad: () => console.log('loaded'),
onError: (e) => console.error(e),
}"
class="hero"
/>Опции
| Опция | Тип | По умолчанию | Описание |
|---|---|---|---|
src | string | — | URL фонового изображения |
placeholder | string | — | Base64 или URL, показываемый сразу; заменяется после загрузки |
rootMargin | string | "200px" | rootMargin для IO |
threshold | number | 0 | threshold для IO |
onLoad | () => void | — | Вызывается при завершении загрузки изображения |
onError | (e: Event) => void | — | Вызывается при неудаче загрузки изображения |
Поведение
- При монтировании — создаётся
IntersectionObserverи начинает наблюдать за элементом - Когда элемент попадает во вьюпорт — если задан
placeholder, он немедленно применяется какbackground-image - Новый объект
Imageзагружаетsrcв фоне - При загрузке —
background-imageобновляется наsrc; вызываетсяonLoad - При ошибке — вызывается
onError;background-imageостаётся плейсхолдером (если он был) - При размонтировании — observer отключается
- При обновлении биндинга — observer пересоздаётся с новыми опциями
Регистрация директивы вручную
Директива регистрируется автоматически вместе с VImageKitPlugin. Чтобы зарегистрировать её в одном компоненте:
<script setup lang="ts">
import { vLazyImg } from 'vue-image-kit'
</script>
<template>
<div v-lazy-img="'/bg.jpg'" style="width:100%;height:400px" />
</template>Или глобально без плагина:
import { vLazyImg } from 'vue-image-kit'
app.directive('lazy-img', vLazyImg)Пример — карточка с ленивым фоном
<script setup lang="ts">
import { vLazyImg } from 'vue-image-kit'
const cards = [
{ id: 1, bg: '/card-1.jpg', placeholder: 'data:image/jpeg;base64,/9j/...' },
{ id: 2, bg: '/card-2.jpg', placeholder: 'data:image/jpeg;base64,/9j/...' },
]
</script>
<template>
<div
v-for="card in cards"
:key="card.id"
v-lazy-img="{ src: card.bg, placeholder: card.placeholder }"
class="card"
/>
</template>
<style scoped>
.card {
width: 300px;
height: 200px;
background-size: cover;
background-position: center;
border-radius: 12px;
}
</style>useBackgroundImage
Директива v-lazy-img лениво загружает фон, но не умеет srcset. useBackgroundImage — composable-аналог: ленивая загрузка + адаптивный image-set() (CSS-нативный эквивалент srcset) + blur-up — возвращается как реактивный :style, который вы биндите сами.
<script setup lang="ts">
import { useBackgroundImage } from 'vue-image-kit'
const { target, style, isLoaded } = useBackgroundImage('/hero.jpg', {
placeholder: 'data:image/jpeg;base64,/9j/...',
densities: [1, 2], // → image-set(url("/hero.jpg") 1x, url("/hero.jpg") 2x)
rootMargin: '300px',
})
</script>
<template>
<section ref="target" :style="style" class="hero">
<h1 v-show="isLoaded">Welcome</h1>
</section>
</template>
<style scoped>
.hero {
width: 100%;
height: 60vh;
}
</style>Опции
| Опция | Тип | По умолчанию | Описание |
|---|---|---|---|
placeholder | string | — | URL/data URL, показываемый (размытым) до загрузки полного изображения |
densities | number[] | — | Строит адаптивный image-set() с записями 1x/2x/… |
type | string | — | MIME-подсказка для записей image-set() (например, 'image/webp') |
lazy | boolean | true | Отложить загрузку за IntersectionObserver |
rootMargin | string | '200px' | Корневой отступ IO |
threshold | number | 0 | Порог IO |
transition | string | '0.4s ease' | Переход blur-up |
backgroundSize | string | 'cover' | background-size |
backgroundPosition | string | 'center' | background-position |
Возвращает { target, style, status, isLoaded, isLoading, load }. Прикрепите target через template-ref и забиндите style; вызовите load() для ручного запуска при lazy: false. SSR-безопасно (загрузка откладывается до клиента).
Vue-плагин
Зарегистрируйте <VImage> и v-lazy-img глобально одним вызовом app.use():
import { createApp } from 'vue'
import { VImageKitPlugin } from 'vue-image-kit'
import App from './App.vue'
const app = createApp(App)
app.use(VImageKitPlugin)
app.mount('#app')После установки:
<VImage>доступен во всех шаблонах без импорта- Директива
v-lazy-imgзарегистрирована и доступна во всех шаблонах
Импортируйте плагин и отдельные экспорты по отдельности при необходимости:
import {
VImageKitPlugin, // Vue-плагин
VImage, // компонент
vLazyImg, // директива
useImage, // composable
useBlurhash, // composable для canvas
useLazyLoad, // composable для IO
decodeBlurhash, // автономный декодер
generateSrcset, // утилита srcset
generateSizes, // утилита sizes
} from 'vue-image-kit'