CLI — генерация изображений
Изменение размера изображений, конвертация в WebP/AVIF, генерация LQIP и BlurHash, запись TypeScript-манифеста — всё одной командой.
Требует sharp как dev-зависимость:
npm install sharp --save-devБазовое использование:
npx vue-image-kit generate \
--input ./src/images \
--output ./public/images \
--widths 400,800,1200 \
--formats jpg,webp,avif \
--manifest ./src/assets/images.tsВыводит отчёт по каждому изображению по мере работы — путь/формат/размеры/размер файла исходника, затем каждый выходной вариант со своим путём/форматом/размерами/размером файла ((existing) для файла, оставленного --skip-existing, (dry-run — not written) при --dry-run) — затем итог по пакету: количество обработанных изображений/файлов, общий входной и выходной размер, и насколько наименьший доступный формат экономит по сравнению с оригиналом в среднем (намеренно не «общий выход против общего входа» — при нескольких ширинах × форматах на изображение общий выход естественным образом в разы больше размера одного оригинала, что вводило бы в заблуждение, читаясь как «стало хуже»):
[vue-image-kit] Processing 1 image(s)…
[vue-image-kit] photo1
Input ./src/images/photo1.jpg
jpg · 1200×800 · 245.3 KB
Output
./public/images/photo1-400.jpg jpg 400×267 52.1 KB
./public/images/photo1-800.jpg jpg 800×533 118.4 KB
./public/images/photo1.jpg jpg 1200×800 198.2 KB
./public/images/photo1.webp webp 1200×800 112.9 KB
./public/images/photo1.avif avif 1200×800 79.6 KB
[vue-image-kit] Done. 1 image(s) → 5 file(s).
Input: 1 image(s), 245.3 KB total
Output: 5 file(s), 561.2 KB total
Smallest available format saves ~79% vs. original, on average
[vue-image-kit] Manifest written to ./src/assets/images.tsВсе опции:
| Флаг | По умолчанию | Описание |
|---|---|---|
--input <dir> | ./src/images | Директория-источник |
--output <dir> | ./public/images | Выходная директория |
--widths <list> | 400,800,1200 | Выходные ширины через запятую |
--formats <list> | jpg,webp,avif | Выходные форматы |
--quality <json> | {"jpg":85,"webp":80,"avif":65} | Качество по формату |
--template <str> | {name}-{width}.{ext} | Шаблон имени файла ({name}, {width}, {ext}) |
--manifest <path> | — | Записать манифест images.ts по этому пути |
--public-path <str> | /images | URL-префикс, используемый в путях манифеста |
--lqip / --no-lqip | включено | Генерировать base64 LQIP-плейсхолдер |
--blurhash / --no-blurhash | включено | Генерировать строку BlurHash |
--thumbhash / --no-thumbhash | выключено | Генерировать строку ThumbHash (требует dev-зависимость thumbhash) |
--clean | — | Удалить выходную директорию перед генерацией |
--dry-run | — | Предпросмотр без записи файлов |
--skip-existing | — | Пропускать уже сгенерированные файлы |
--concurrency <n> | 4 | Количество параллельных воркеров |
--watch | — | Отслеживать входную директорию и перегенерировать при изменении |
--incremental / --no-incremental | авто | Пропускать переобработку источника, если его mtime (или, если он изменился, хэш содержимого) совпадает с предыдущим запуском. Автоматически включается под --watch (и плагином Vite во время vite dev), если не задано явно тем или иным способом — разовый generate по умолчанию выключен. См. Инкрементальная генерация ниже |
Файл конфигурации — создайте vue-image-kit.config.js в корне проекта, чтобы не повторять флаги:
// vue-image-kit.config.js
export default {
input: './photos',
output: './public/images',
widths: [480, 960, 1440],
formats: ['jpg', 'webp'],
manifest: './src/assets/images.ts',
publicPath: '/images',
}SVG и анимированный GIF обрабатываются иначе, чем растровые форматы — они определяются по расширению входного файла, а не по --formats:
- SVG копируется без изменений (без растеризации — он уже разрешение-независим). Запись манифеста получает
src, указывающий на копию;webp/avif/placeholder/blurhash/thumbhash— пустые строки. - Анимированный GIF копируется как гарантированно совместимый fallback (
src), и — когдаwebpв--formats— перекодируется в анимированный WebP (полеwebp) для реальной экономии размера. AVIF пропускается: поддержка анимированного AVIF в сборкахsharp/libavif слишком непоследовательна, чтобы на неё полагаться. Плейсхолдеры LQIP/BlurHash/ThumbHash всё равно генерируются из первого кадра.
Инкрементальная генерация
--watch и buildStart/handleHotUpdate плагина Vite вызывают одну и ту же функцию generate(), что и CLI, — по умолчанию каждое изменение одного файла означает повторное сканирование и обработку всех исходных изображений, а не только того, что изменилось. Режим incremental это исправляет:
npx vue-image-kit generate --watch --incremental # уже по умолчанию под --watchДля каждого источника проверяется сохранённая запись из предыдущего запуска: mtime не изменился → полностью пропускается, без чтения, без вызова sharp. Mtime изменился (например, git checkout затронул все файлы) → используется хэш содержимого перед принятием решения — неизменённый файл переживает checkout, не вызывая ненужную переобработку. Запись — JSON-манифест по пути <output>/.vik-incremental.json — также хранит полные метаданные каждого пропущенного изображения, так что отчёт по пакету/вывод --manifest остаётся полным даже для изображений, не тронутых в этом запуске.
Изменение widths/formats/quality/template/publicPath/lqip/ blurhash/thumbhash между запусками инвалидирует всё сразу (логируется как Config changed since last run) — не файл-за-файлом, поскольку изменение конфига может повлиять на любой или все выходы. --clean тоже инвалидирует всё, естественным образом: удаляет output, а манифест живёт внутри неё. Нет эффекта под --dry-run (ничего не записывается, поэтому нет ничего валидного для сравнения в следующий раз).
По умолчанию: выключено для разового generate (один запуск ничего не выигрывает от кэширования), автоматически включается под --watch и во время vite dev (vite build остаётся выключенным — продакшен-артефакт не должен рисковать устаревшим кэшем). Явный --incremental/--no-incremental (флаг CLI, файл конфигурации или опция плагина Vite) всегда переопределяет автоматическое значение по умолчанию в любую сторону.