Skip to content

Плагин Vite ​

Обрабатывайте изображения на этапе сборки — так же, как CLI, но интегрировано в жизненный цикл Vite. Запускается на buildStart и перезапускается в dev-режиме при изменении исходных изображений.

Рецепты интеграции показывают готовые конфигурации на все случаи — с пояснением, для чего каждая и когда её применять.

ts
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { vueImageKit } from '@macrulez/vue-image-kit/vite'

export default defineConfig({
  plugins: [
    vue(),
    vueImageKit({
      input: './src/images',
      output: './public/images',
      widths: [400, 800, 1200],
      manifest: './src/assets/images.ts',
    }),
  ],
})

Поддерживаются все опции CLI. sharp должен быть установлен как dev-зависимость.

buildStart/handleHotUpdate вызывают ту же generate(), что и CLI, поэтому инкрементальная генерация применима и здесь — и автоматически включается именно во время vite dev (не vite build), если не задать incremental явно. Изменение одного исходного файла в dev переобрабатывает только этот файл, а не весь пакет.

Горячее обновление запускает генерацию только для изображения внутри input и никогда для файла внутри output, поэтому файлы, которые плагин пишет сам, не могут запустить новый проход. Проходы никогда не накладываются друг на друга: изменения, пришедшие во время работы, объединяются в один повторный запуск. output может даже лежать внутри input — при сканировании папки он пропускается.

SVG-изображения ​

У векторного изображения нет hazehash, blurhash и thumbhash — только доминирующий цвет и размер:

ts
import icon from './logo.svg?placeholder'
// → { placeholderColor: '#ee3333', width: 64, height: 64 }

Размер берётся из собственных width/height (или viewBox) файла без растеризации, а большой SVG для определения цвета рендерится в уменьшенном виде, поэтому вектор 30000×30000 не упирается в лимит пикселей sharp. ?hazehash, ?blurhash и ?thumbhash для SVG возвращают пустую строку и печатают предупреждение со ссылкой на ?placeholder. Битый SVG не роняет сборку: плагин предупреждает и возвращает объявленный размер (или пустой объект). Битое растровое изображение по-прежнему падает с ошибкой.

Импорты на этапе сборки ​

Плагин также разрешает импорты с суффиксом запроса, так что вам никогда не нужно вручную настраивать пропы — метаданные попадают прямо в ваш JS на этапе сборки:

ts
import meta from './photo.jpg?vik'
// → { src, srcset, webp, avif, width, height, placeholder, blurhash, thumbhash, name, src400, ... }

import hash from './photo.jpg?thumbhash'
// → 'base64string'

Передавайте метаданные прямо в проп image компонента <VImage> — без ручной настройки полей:

vue
<script setup lang="ts">
import meta from './hero.jpg?vik'
</script>

<template>
  <VImage :image="meta" alt="Hero" />
</template>
  • ?vik изменяет размер/кодирует изображение в output и возвращает полную запись манифеста (URL используют publicPath, точно как в сгенерированном манифесте). ThumbHash включается всегда.
  • ?placeholder (со списком полей), ?hazehash, ?blurhash, ?thumbhash, ?color, ?size, ?aspect и ?preview вычисляют только данные плейсхолдера и не пишут файлы — см. Только плейсхолдеры и Выбор того, что считать.

Все они перезапускаются при изменении исходного изображения в dev. Требуется sharp; thumbhash требуется для вывода ThumbHash.

TypeScript — включите типизированные импорты ?vik / ?placeholder / ?hazehash / ?blurhash / ?thumbhash, один раз сославшись на встроенные декларации (например, в env.d.ts):

ts
/// <reference types="@macrulez/vue-image-kit/vite/client" />

Только плейсхолдеры, без изменения размеров ​

Если адаптивные варианты не нужны, а нужен только блюр на время загрузки, отключите пакетную обработку через generate: false. Плагин ничего не делает сам и только отвечает на импорты плейсхолдеров и на манифест плейсхолдеров, поэтому папка input может не существовать:

ts
// vite.config.ts
vueImageKit({ generate: false })
vue
<script setup lang="ts">
import placeholder from './photo.jpg?placeholder'
</script>

<template>
  <VImage src="/photo.jpg" v-bind="placeholder" alt="Photo" />
</template>

?placeholder возвращает готовые для v-bind пропсы <VImage> — { hazehash, placeholderColor, width, height } (вместо hazehash — blurhash, если пакет не установлен), посчитанные при сборке по настоящему файлу. Какой хеш в них попадёт, задаёт значение:

  • ?placeholder — HazeHash (BlurHash, если пакет hazehash не установлен или mode равен blurhash), доминирующий цвет и исходный размер.
  • ?placeholder=hazehash — явно HazeHash.
  • ?placeholder=blurhash — явно BlurHash.
  • ?placeholder=thumbhash — ThumbHash вместо BlurHash.
  • ?placeholder=color — только доминирующий цвет и размер.
  • ?hazehash / ?blurhash / ?thumbhash — только строка хеша, export default 'LJ9+E%}}…'.

Значения совпадают с теми, что для того же файла считает команда placeholders. При generate: false импорт ?vik по-прежнему работает и, если вы его используете, пишет свои варианты в output.

Выбор того, что считать ​

?placeholder принимает список полей, так что один импорт возвращает ровно то, что нужно:

ts
import ph from './photo.jpg?placeholder=color,blurhash'
// → { blurhash: 'L6T9R{,YfQ,Y|cjtfQjtfQfQfQfQ', placeholderColor: '#fe0000' }

import all from './photo.jpg?placeholder=all&shape=raw'
// → { hazehash, blurhash, thumbhash, color, preview, size: { width, height }, aspect }

Поля разделяются запятыми (пробелы тоже подходят), порядок любой, а поле, названное дважды, считается один раз:

  • hazehash — строка HazeHash (нужен пакет hazehash).
  • blurhash — строка BlurHash.
  • thumbhash — ThumbHash в base64.
  • color — доминирующий цвет как #rrggbb (у изображения без единого непрозрачного пикселя цвета нет, и поле опускается).
  • size — реальные width и height.
  • preview — крошечный PNG в виде URI data:image/png;base64,…, восстановленный из ThumbHash. Его можно сразу подставить в src или в CSS background, ничего не декодируя на странице.
  • aspect — width / height числом, например 2 для изображения 200×100.
  • all — все поля выше.

Возвращаются только перечисленные поля. Если <VImage> нужны width и height, добавьте и size. Старые одиночные значения сохраняют свою форму: ?placeholder, ?placeholder=hazehash, ?placeholder=blurhash, ?placeholder=thumbhash и ?placeholder=color по-прежнему возвращают хеш вместе с цветом и размером.

Форма результата ​

&shape=props (по умолчанию) использует имена пропсов <VImage>, поэтому работает <VImage v-bind="ph" />: color становится placeholderColor, preview — placeholder, size — width и height. aspect — не проп <VImage>, поэтому он пропускается с предупреждением. &shape=raw оставляет имена как в списке и возвращает size объектом, а aspect включает:

ts
import raw from './photo.jpg?placeholder=color,size,aspect&shape=raw'
// → { color: '#fe0000', size: { width: 200, height: 100 }, aspect: 2 }

По одному значению ​

У каждого поля есть и свой импорт, который возвращает голое значение:

ts
import color from './photo.jpg?color' // '#fe0000'
import size from './photo.jpg?size' // { width: 200, height: 100 }
import aspect from './photo.jpg?aspect' // 2
import preview from './photo.jpg?preview' // 'data:image/png;base64,…'
import blur from './photo.jpg?blurhash' // 'L6T9R…'
import thumb from './photo.jpg?thumbhash' // '1fsDBI…'

Значение, которое не удалось посчитать, приходит как '' (для ?aspect — 0, для ?size — 0×0). У ?size, ?color, ?preview и ?aspect флаг должен быть голым: ?size=large остаётся нетронутым, так что одноимённый параметр другого плагина не перехватывается.

Настройки для одного импорта ​

Любой из этих импортов принимает настройки из раздела настройка хешей только для этого файла:

  • components — компоненты BlurHash, по умолчанию 4x3, каждая сторона 1–9.
  • sample — размер, до которого изображение уменьшается перед хешированием, 8–256.
  • strategy — dominant (по умолчанию) или average для цвета.
ts
import hash from './photo.jpg?blurhash&components=6x4'
import ph from './photo.jpg?placeholder=blurhash,color&sample=64&strategy=average'

Неверное значение печатает предупреждение, и используется значение по умолчанию. Настройки одного импорта никогда не затрагивают общий кэш и манифест плейсхолдеров.

Типы для списка ​

/// <reference types="@macrulez/vue-image-kit/vite/client" /> типизирует фиксированные импорты (?color, ?size, ?placeholder=color и т. д.). Список или импорт с настройками так не типизировать: в шаблоне модуля TypeScript допускает только одну *, и она уже нужна для пути к файлу. Пусть декларации напишет плагин:

ts
vueImageKit({ types: true })

Плагин запоминает каждый такой импорт, который встретил, и пишет src/vik-imports.d.ts (types: 'path/to/file.d.ts' выбирает другое место). Список типизируется свободно, с необязательными полями (hazehash?: string, blurhash?: string, placeholderColor?: string, …), поэтому порядок и набор полей не важны. Записи только добавляются и никогда не удаляются, поэтому dev-сервер, загрузивший лишь часть страниц, не урезает файл; чтобы пересоздать его, удалите файл. Пока импорт не встретился хотя бы раз, редактор сообщает о неизвестном модуле.

Примеры ​

Размытие и размер для одного изображения. size задаёт <VImage> его width и height, поэтому страница не прыгает, когда изображение загрузится:

vue
<script setup lang="ts">
import photo from './assets/photo.jpg'
import ph from './assets/photo.jpg?placeholder=blurhash,size'
</script>

<template>
  <VImage :src="photo" v-bind="ph" alt="Photo" />
</template>

Готовое превью как CSS-фон. ?preview — это data URI крошечного PNG, так что на странице ничего не нужно декодировать:

vue
<script setup lang="ts">
import hero from './assets/hero.jpg'
import preview from './assets/hero.jpg?preview'
</script>

<template>
  <section class="hero" :style="{ backgroundImage: `url(${preview})` }">
    <img :src="hero" alt="" loading="lazy" />
  </section>
</template>

Заранее зарезервировать место через соотношение сторон.

vue
<script setup lang="ts">
import cover from './assets/card.jpg'
import aspect from './assets/card.jpg?aspect'
</script>

<template>
  <div :style="{ aspectRatio: aspect }">
    <img :src="cover" alt="" />
  </div>
</template>

Несколько значений сразу, под их собственными именами.

ts
import raw from './assets/photo.jpg?placeholder=color,size,aspect&shape=raw'

const { color, size, aspect } = raw
console.log(color, size.width, size.height, aspect)

Целая папка через import.meta.glob. Запрос — та же строка:

ts
const placeholders = import.meta.glob('./assets/gallery/*.jpg', {
  query: '?placeholder=blurhash,size',
  import: 'default',
  eager: true,
})

Настройки только для одного изображения.

ts
import hash from './assets/banner.jpg?blurhash&components=8x3'
import avg from './assets/banner.jpg?color&strategy=average'

Типы для списков. Один раз подключите встроенные декларации, а остальное допишет плагин:

ts
// vite.config.ts
vueImageKit({ generate: false, types: true })
ts
// env.d.ts
/// <reference types="@macrulez/vue-image-kit/vite/client" />

SVG ​

У векторного изображения есть только color, size и aspect. Остальные поля пропускаются с предупреждением. См. SVG-изображения.

Параметры плейсхолдеров ​

Все настройки плейсхолдеров лежат под одним ключом, placeholders: в опциях Vite-плагина или Nuxt-модуля. Здесь они собраны вместе, с описанием, за что отвечает каждый; разделы ниже объясняют отдельные части подробнее.

В проекте на Vite ​

ts
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { vueImageKit } from '@macrulez/vue-image-kit/vite'

export default defineConfig({
  plugins: [
    vue(),
    vueImageKit({
      generate: false,
      types: true,
      placeholders: {
        mode: 'blurhash',
        tuning: { components: [4, 3], sample: 100, color: 'dominant' },
        imports: {
          extensions: ['.png', '.jpg', '.jpeg', '.webp', '.avif'],
          exclude: ['src/assets/sprites/'],
          preview: ['src/assets/hero/', '**/cover-*.webp'],
        },
        dirs: ['public/images', { dir: 'src/img', urlPrefix: '/assets/img' }],
        urls: ['https://cdn.example.com/hero.jpg'],
        root: '.',
        publicDir: 'public',
        concurrency: 4,
        timeout: 15000,
        maxBytes: 15 * 1024 * 1024,
        refreshRemote: false,
      },
    }),
  ],
})

В проекте на Nuxt ​

Модуль сам регистрирует Vite-плагин, поэтому тот же объект placeholders кладётся под vueImageKit в nuxt.config.ts. generate: false выставляется за вас, а относительные пути считаются от корня Nuxt:

ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@macrulez/vue-image-kit/nuxt'],
  vueImageKit: {
    placeholders: {
      mode: 'blurhash',
      tuning: { components: [4, 3], sample: 100, color: 'dominant' },
      imports: {
        extensions: ['.png', '.jpg', '.jpeg', '.webp', '.avif'],
        exclude: ['src/assets/sprites/'],
        preview: ['src/assets/hero/', '**/cover-*.webp'],
      },
      dirs: ['public/images', { dir: 'src/img', urlPrefix: '/assets/img' }],
      urls: ['https://cdn.example.com/hero.jpg'],
      manifest: './image-placeholders.ts',
    },
  },
})

Всё сразу нужно редко. Минимальная полезная настройка — placeholders: { imports: true }; preview добавляйте только для картинок, которым нужен blur уже в HTML от сервера.

Опции Vite-плагина, которые здесь важны ​

  • generate — boolean · по умолчанию: true. Запускать пакетную обработку input в output. false оставляет только возможности плейсхолдеров: плагин не трогает ни один файл, а input может не существовать. Nuxt-модуль всегда ставит false для плагина плейсхолдеров.
  • types — boolean | string · по умолчанию: выключено. Записать .d.ts для импортов со списком полей или настройками; true пишет src/vik-imports.d.ts, строка задаёт другой путь. См. Типизация списка.
  • placeholders — object. Всё, что ниже.

placeholders ​

  • imports — boolean | object · по умолчанию: выключено. Считать плейсхолдер для каждого изображения, которое скрипт или однофайловый компонент импортирует, без запроса и правок шаблонов. true берёт значения по умолчанию для трёх настроек ниже; объект их настраивает. См. Плейсхолдер для каждого импортируемого изображения.
    • imports.extensions — string[] · по умолчанию: ['.png', '.jpg', '.jpeg', '.webp', '.avif', '.gif', '.tif', '.tiff']. Какие типы файлов брать. SVG не берётся никогда.
    • imports.exclude — string[] · по умолчанию: []. Изображения, которые нужно пропустить. Список шаблонов путей.
    • imports.preview — boolean | string[] · по умолчанию: false. Дополнительно сделать готовое превью data: и зарегистрировать его, чтобы <VImage ssr-placeholder> мог положить его в HTML от сервера. true — каждое импортируемое изображение; список шаблонов путей — только выбранные им. Нужен пакет thumbhash. См. Готовое превью для серверного рендеринга.
  • mode — 'hazehash' | 'blurhash' | 'thumbhash' | 'color' · по умолчанию: 'hazehash', если установлен пакет hazehash, иначе 'blurhash'. Что несёт плейсхолдер кроме доминирующего цвета и размера: HazeHash, BlurHash, ThumbHash или ничего больше. Это режим автоматических imports, голого импорта ?placeholder и каждой записи манифеста.
  • tuning — { components?, sample?, color?, budget? }. Как считаются хеши и цвета: компоненты BlurHash (по умолчанию [4, 3]), размер, до которого уменьшается изображение перед хешированием (по умолчанию 100), и 'dominant' или 'average' для цвета (по умолчанию 'dominant'). См. Настройка хешей.
  • dirs — (string | { dir, urlPrefix? })[]. Папки, изображения которых получают запись в манифесте плейсхолдеров (virtual:vue-image-kit/placeholders), для картинок, чей src строится в рантайме. urlPrefix — URL, под которым папка отдаётся, если она лежит вне публичной директории.
  • urls — string[]. Удалённые или CDN-изображения, которые скачиваются на этапе сборки и получают запись манифеста.
  • manifest — string, только Nuxt. Файл манифеста (тот, что пишет npx vue-image-kit placeholders) для слияния с папками; его записи главнее.
  • root — string · по умолчанию: корень проекта. Директория, от которой считаются относительные dirs. Nuxt ставит корень Nuxt. (Шаблоны exclude и preview всегда считаются от корня проекта Vite.)
  • publicDir — string · по умолчанию: публичная директория Vite (public). Файлы внутри неё попадают в манифест под своим URL-путём.
  • concurrency — number · по умолчанию: 4. Сколько изображений обрабатывается параллельно.
  • timeout — number · по умолчанию: 15000. Сколько миллисекунд ждать удалённое изображение.
  • maxBytes — number · по умолчанию: 15728640 (15 МБ). Удалённое изображение больше этого размера пропускается.
  • refreshRemote — boolean · по умолчанию: false. Скачивать удалённые изображения заново, даже если есть копия в кэше.

Результаты кэшируются в node_modules/.cache/vue-image-kit/placeholders.json; локальный файл пересчитывается только при изменении времени правки или размера, а смена tuning сбрасывает кэш.

Плейсхолдер для каждого импортируемого изображения ​

Если проект импортирует свои изображения (import hero from './hero.webp' или просто src="@/assets/hero.webp" в шаблоне), включите imports — и у каждого из них при сборке появится плейсхолдер, без запроса и без правки каких-либо шаблонов:

ts
// vite.config.ts
vueImageKit({
  generate: false,
  placeholders: { imports: true },
})
ts
// nuxt.config.ts
vueImageKit: {
  placeholders: { imports: true },
}
vue
<script setup lang="ts">
import hero from '@/assets/hero.webp'
</script>

<template>
  <VImage :src="hero" alt="Hero" />
</template>

<VImage> сам находит размытие и реальный размер. Плагин считает их по файлу, а затем регистрирует под значением, которое вернул импорт, — dev-URL или хешированный URL в продакшен-сборке, — поэтому поиск по src работает в dev, в сборке и при серверном рендеринге.

Что охвачено: default-импорты файлов .png, .jpg, .jpeg, .webp, .avif, .gif и .tif в скриптах и однофайловых компонентах, импорты, которые Vue сам генерирует из src="…" в шаблоне, и import.meta.glob(…, { eager: true }) (Vite разворачивает его в импорты). Не охвачено: SVG, импорты с запросом (?url), ленивые глобы (import()) и URL, собираемые во время выполнения. Для них импортируйте с ?placeholder или используйте манифест из папок.

Настройки идут рядом с imports:

ts
placeholders: {
  mode: 'thumbhash',
  imports: {
    extensions: ['.png', '.webp'],
    exclude: ['/sprites/', 'canvas-'],
  },
}
  • mode — hazehash (по умолчанию, если установлен пакет hazehash), blurhash (по умолчанию в остальных случаях) или thumbhash, та же опция, что и у остальных плейсхолдеров; для thumbhash нужен пакет thumbhash.
  • extensions — какие типы файлов брать.
  • exclude — изображения, которые нужно пропустить, для тех, что никогда не показываются через <VImage> (например, спрайт для canvas). Принимает те же шаблоны путей, что и preview.
  • preview — true (каждое импортируемое изображение) или список шаблонов путей: дополнительно сделать готовое превью data:. См. Готовое превью для серверного рендеринга. По умолчанию выключено.

Битое изображение печатает предупреждение и просто остаётся без плейсхолдера; сборку оно не роняет. sharp должен быть установлен как dev-зависимость, а первая сборка считает каждое изображение один раз и хранит результат в том же кэше, что и остальные плейсхолдеры.

Выбор изображений по пути ​

exclude и preview принимают список шаблонов. Каждый шаблон сравнивается с настоящим путём файла относительно корня проекта (src/assets/hero/a.webp), поэтому неважно, как файл импортирован: @/assets/hero/a.webp, ~/assets/… и ../assets/… указывают на один и тот же путь. В dev-сервере берётся путь, который запрашивает браузер, без base.

  • Директория — шаблон, заканчивающийся на /: 'src/assets/hero/' выбирает всё внутри этой директории на любой глубине.
  • Точный файл — полный относительный путь: 'src/assets/hero/cover.webp'.
  • Часть пути — любой другой текст ищется внутри пути: 'hero', '-large', '/sprites/'.
  • Glob — шаблон со * сравнивается со всем путём целиком: * означает любые символы внутри имени одной директории, ** — любое число директорий. 'src/assets/*.png' выбирает PNG прямо в src/assets, 'src/**/hero-*.webp' — подходящие файлы где угодно под src.

Ведущие ./ и / в шаблоне игнорируются. Если путь вычислить не удалось (импорт, который сборщик не может разрешить), шаблон сравнивается с текстом импорта как он записан.

Готовое превью для серверного рендеринга ​

Blur, который регистрирует imports, — это хеш: он декодируется в браузере, поэтому появляется только после запуска JavaScript. Для картинок, у которых blur должен быть уже в HTML от сервера, запросите ещё и готовое превью:

ts
placeholders: {
  imports: {
    preview: ['src/assets/hero/', '**/cover-*.webp'],
  },
}
  • preview: true — превью для каждого импортируемого изображения.
  • preview: ['src/assets/hero/', '**/cover-*.webp'] — только для картинок, которые выбирают шаблоны: по директории, по точному файлу или по glob (см. Выбор изображений по пути). Так бандл остаётся небольшим.
  • не задано / false — превью нет (по умолчанию).

Плагин рисует каждое превью (небольшой PNG как data: URL) и регистрирует его вместе с остальными полями, но само в HTML оно не попадает. Картинка просит об этом явно через ssrPlaceholder:

vue
<VImage :src="hero" alt="Hero" ssr-placeholder />

Тогда сервер отрендерит <img> с превью в качестве фона; любой <VImage> без пропса остаётся как был. Превью добавляет несколько килобайт на картинку в бандл (и в HTML страниц, где она используется), поэтому это отдельный переключатель, а для большого проекта список лучше, чем true.

Превью рисуется из ThumbHash, поэтому пакет thumbhash должен стоять как dev-зависимость, даже если mode равен blurhash. Для одной картинки без imports импортируйте превью напрямую — import preview from './hero.webp?preview' — и передайте как :placeholder="preview". См. Превью в серверном HTML.

Превью для серверного рендеринга: шаг за шагом ​

  1. Установите зависимости. Превью рисуется из ThumbHash через sharp, поэтому оба пакета нужны на этапе сборки, каким бы ни был mode:

    bash
    npm install -D sharp thumbhash
  2. Включите превью в конфиге для тех картинок, которым оно нужно. В Vite:

    ts
    // vite.config.ts
    vueImageKit({
      generate: false,
      placeholders: {
        imports: { preview: ['src/assets/hero/'] },
      },
    })

    В Nuxt:

    ts
    // nuxt.config.ts
    export default defineNuxtConfig({
      modules: ['@macrulez/vue-image-kit/nuxt'],
      vueImageKit: {
        placeholders: {
          imports: { preview: ['src/assets/hero/'] },
        },
      },
    })

    Именно imports.preview заставляет плагин сделать превью и сохранить его в реестре. Без него ssr-placeholder нечего показывать.

  3. Отметьте картинки, которые должны его показать, пропсом ssr-placeholder у <VImage>. Изображение импортируется как обычно:

    vue
    <script setup lang="ts">
    import hero from '@/assets/hero/cover.webp'
    </script>
    
    <template>
      <VImage :src="hero" alt="Cover" :width="1200" :height="600" ssr-placeholder />
    </template>
  4. Перезапустите dev-сервер — опции модуля и плагина читаются при старте.

  5. Проверьте HTML, который отдаёт сервер. У ленивой картинки вместо настоящего src прозрачный пиксель, превью лежит фоном, а настоящее изображение — в <noscript>:

    html
    <img
      src="data:image/gif;base64,R0lGOD…"
      alt="Cover"
      width="1200"
      height="600"
      loading="lazy"
      style="background-image:url(data:image/png;base64,iVBOR…);background-size:cover;…"
    />
    <noscript><img src="/_nuxt/cover.3f9a.webp" alt="Cover" width="1200" height="600" /></noscript>

    Если в src настоящий файл, а background-image нет, у картинки нет превью: проверьте, что файл подходит под шаблон в imports.preview, что установлен thumbhash и что сервер перезапущен.

Какие картинки получают превью (конфиг) и какие <VImage> его используют (проп) — разные вещи намеренно: на этапе сборки плагин не знает, какой компонент получает какое изображение, а превью стоит несколько килобайт, поэтому оно делается только там, где вы о нём просите.

Манифест плейсхолдеров из папок ​

Задайте placeholders.dirs, и плагин посчитает манифест плейсхолдеров для каждого изображения в этих папках — в том числе для тех, чей src собирается во время выполнения. Манифест — это экспорт по умолчанию виртуального модуля virtual:vue-image-kit/placeholders:

ts
vueImageKit({
  generate: false,
  placeholders: {
    dirs: ['public/images', { dir: 'src/img', urlPrefix: '/assets/img' }],
    urls: ['https://cdn.example.com/hero.jpg'],
    mode: 'blurhash',
  },
})
ts
import placeholders from 'virtual:vue-image-kit/placeholders'

app.use(VImageKitPlugin, { placeholders })
  • dirs — (string | { dir, urlPrefix })[]. Папки, которые сканируются рекурсивно, пути — относительно корня проекта. Файл из публичной папки записывается по пути от неё; папке вне её нужен urlPrefix — без него файлы записываются по пути в проекте (/src/img/a.jpg), который совпадает только с URL dev-сервера, и сборка об этом предупредит.
  • urls — string[]. Удалённые или CDN-изображения, они скачиваются во время сборки; CDN-адреса запрашиваются в уменьшенной версии.
  • mode — 'hazehash' | 'blurhash' | 'thumbhash' | 'color' · по умолчанию: hazehash, если пакет установлен, иначе blurhash. Что хранит каждая запись, кроме доминирующего цвета и размера. Это же значение по умолчанию для импорта ?placeholder без значения.
  • tuning — см. Настройка хешей.
  • publicDir, concurrency, timeout, maxBytes, refreshRemote — публичная папка (по умолчанию public) и те же ограничения загрузки, что у команды placeholders.

Модуль пересобирается, когда файл из dirs добавлен, удалён или изменён во время vite dev, а результаты кешируются в node_modules/.cache/vue-image-kit/placeholders.json — тот же кеш использует команда placeholders, — поэтому повторная сборка читает только изменившиеся изображения. Импорты ?placeholder, ?blurhash и ?thumbhash тоже используют этот кеш.

Тип модуля поставляется вместе с остальными декларациями @macrulez/vue-image-kit/vite/client.

Настройка хешей ​

Везде, где считается плейсхолдер, — импорты плагина ?placeholder/?blurhash/?thumbhash и его манифест, команда placeholders и опция Nuxt — принимается один и тот же объект tuning:

ts
vueImageKit({
  generate: false,
  placeholders: {
    dirs: ['public/images'],
    tuning: { components: [6, 4], sample: 128, color: 'average' },
  },
})
  • components — [x, y] · по умолчанию: [4, 3]. Число компонент BlurHash по каждой оси, от 1 до 9. Больше компонент — больше деталей и длиннее строка.
  • budget — number · по умолчанию: 28. Размер строки HazeHash в байтах, 7–48. Для одного импорта — ?hazehash&budget=16.
  • sample — number · по умолчанию: 100. Длиннейшая сторона в пикселях, до которой изображение уменьшается перед расчётом хеша; от 8 до 256. ThumbHash всегда считается не больше чем по 100px.
  • color — 'dominant' | 'average' · по умолчанию: 'dominant'. Как выбирается цвет плейсхолдера: самый частый цвет или среднее по всем непрозрачным пикселям.

При смене настроек кеш сбрасывается, так что устаревший хеш не используется.

Обслуживание по запросу в dev-режиме ​

И CLI, и импорты на этапе сборки выше — это пакетная/заблаговременная обработка — они обрабатывают изображения до того, как их запросят. Если вы не хотите запускать шаг сборки вообще во время разработки, установите dev.onDemand: true, и изображения будут изменять размер по запросу, кэшируясь на диск после первого попадания:

ts
vueImageKit({
  dev: { onDemand: true }, // монтирует обработчик на /_vik/image во время `vite dev`
})
html
<img src="/_vik/image?src=/photos/cat.jpg&w=800&format=webp" />

Это работает только в dev — configureServer (хук Vite, который он использует) никогда не запускается во время vite build. Для продакшена без CDN смонтируйте тот же обработчик в своём собственном сервере — см. Самостоятельно размещаемый сервер по запросу.

Опции dev ​

Обработчик за dev.onDemand — тот же createImageHandler, что использует самостоятельный сервер — root фиксирован на собственном корне проекта Vite (server.config.root), всё остальное — та же поверхность настройки:

  • onDemand — boolean · по умолчанию: false. Монтирует обработчик во время vite dev.
  • route — string · по умолчанию: /_vik/image. Маршрут, на который монтируется.
  • cacheDir — string · по умолчанию: <root>/.vik-cache. См. Самостоятельно размещаемый сервер по запросу → Опции.
  • maxAge — number · по умолчанию: 31536000 (1 год).
  • allowedWidths — number[], опционален. Ограничить w строго этими значениями.
  • maxWidth — number · по умолчанию: 4000. Верхняя граница для w, когда allowedWidths не задан.