Skip to content

Composables, директива и плагин

useImage

Headless composable. Используйте, когда нужен конечный автомат состояния загрузки и вычисляемые атрибуты, но требуется своя разметка.

ts
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)

Опции

ОпцияТипПо умолчаниюОписание
srcstring | SrcSetURL изображения или объект формата
widthsnumber[][]Ширины для генерации srcset на основе ширины (w)
densitiesnumber[] | Record<number, string>Дескрипторы плотности (1x/2x/3x); список переиспользует src, объект даёт отдельные файлы; имеет приоритет над widths, игнорирует sizes
sizesstringЗначение атрибута sizes (только для srcset на основе ширины)
lazybooleantrueВключить IntersectionObserver
rootMarginstring"200px"rootMargin для IO
thresholdnumber0threshold для IO
fitObjectFit"cover"Стиль object-fit
maxRetriesnumber0Максимум попыток повтора при неудаче загрузки
retryDelaynumber1000Начальная задержка в мс; удваивается на каждом повторе

Конечный автомат

idle  →  loading  →  loaded
                  →  error
  • При lazy: true — переход в loading, когда наблюдаемый элемент попадает во вьюпорт
  • При lazy: false — переход в loading сразу после onMounted

Возвращаемое значение

СвойствоТипОписание
statusRef<ImageStatus>Текущее состояние загрузки
isLoadedComputedRef<boolean>true, когда status === 'loaded'
isErrorComputedRef<boolean>true, когда status === 'error'
imgAttrsComputedRef<object>{ src, srcset?, sizes?, style } — готов для v-bind
observeFunctionПередайте Ref<HTMLElement>, чтобы начать отслеживание пересечения
onImgLoadFunctionВызывайте из <img @load> для перехода в loaded
onImgErrorFunctionВызывайте из <img @error> для перехода в error

Пример — кастомный рендер

vue
<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-фоны, обёртки сторонних библиотек и т. д.

vue
<!-- Простая строка -->
<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"
/>

Опции

ОпцияТипПо умолчаниюОписание
srcstringURL фонового изображения
placeholderstringBase64 или URL, показываемый сразу; заменяется после загрузки
rootMarginstring"200px"rootMargin для IO
thresholdnumber0threshold для IO
onLoad() => voidВызывается при завершении загрузки изображения
onError(e: Event) => voidВызывается при неудаче загрузки изображения

Поведение

  1. При монтировании — создаётся IntersectionObserver и начинает наблюдать за элементом
  2. Когда элемент попадает во вьюпорт — если задан placeholder, он немедленно применяется как background-image
  3. Новый объект Image загружает src в фоне
  4. При загрузке — background-image обновляется на src; вызывается onLoad
  5. При ошибке — вызывается onError; background-image остаётся плейсхолдером (если он был)
  6. При размонтировании — observer отключается
  7. При обновлении биндинга — observer пересоздаётся с новыми опциями

Регистрация директивы вручную

Директива регистрируется автоматически вместе с VImageKitPlugin. Чтобы зарегистрировать её в одном компоненте:

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

<template>
  <div v-lazy-img="'/bg.jpg'" style="width:100%;height:400px" />
</template>

Или глобально без плагина:

ts
import { vLazyImg } from 'vue-image-kit'

app.directive('lazy-img', vLazyImg)

Пример — карточка с ленивым фоном

vue
<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, который вы биндите сами.

vue
<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>

Опции

ОпцияТипПо умолчаниюОписание
placeholderstringURL/data URL, показываемый (размытым) до загрузки полного изображения
densitiesnumber[]Строит адаптивный image-set() с записями 1x/2x/…
typestringMIME-подсказка для записей image-set() (например, 'image/webp')
lazybooleantrueОтложить загрузку за IntersectionObserver
rootMarginstring'200px'Корневой отступ IO
thresholdnumber0Порог IO
transitionstring'0.4s ease'Переход blur-up
backgroundSizestring'cover'background-size
backgroundPositionstring'center'background-position

Возвращает { target, style, status, isLoaded, isLoading, load }. Прикрепите target через template-ref и забиндите style; вызовите load() для ручного запуска при lazy: false. SSR-безопасно (загрузка откладывается до клиента).

Vue-плагин

Зарегистрируйте <VImage> и v-lazy-img глобально одним вызовом app.use():

ts
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 зарегистрирована и доступна во всех шаблонах

Импортируйте плагин и отдельные экспорты по отдельности при необходимости:

ts
import {
  VImageKitPlugin, // Vue-плагин
  VImage, // компонент
  vLazyImg, // директива
  useImage, // composable
  useBlurhash, // composable для canvas
  useLazyLoad, // composable для IO
  decodeBlurhash, // автономный декодер
  generateSrcset, // утилита srcset
  generateSizes, // утилита sizes
} from 'vue-image-kit'