Skip to content

Vite и Vue ​

Готовые конфигурации плагина Vite для проекта на Vue 3. В каждом рецепте сказано, когда его применять, показан полный конфиг и шаблон и описано, что вы получите. Выбрать рецепт поможет страница Выбор способа.

Для всех рецептов нужен sharp в dev-зависимостях, а там, где считается ThumbHash, ещё и thumbhash:

bash
npm install -D sharp thumbhash

Чтобы импорты были типизированы, один раз сошлитесь на встроенные декларации, например в env.d.ts:

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

Размытие для импортированного изображения ​

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

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 })],
})

generate: false не даёт плагину обрабатывать целую папку при старте, поэтому input настраивать не нужно — плагин только отвечает на импорты ниже.

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

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

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

Варианты.

  • ?placeholder=blurhash — BlurHash вместо HazeHash.
  • ?placeholder=thumbhash — ThumbHash вместо HazeHash (поддерживает прозрачность).
  • ?placeholder=color — только доминирующий цвет и размер, самый лёгкий вариант.
  • ?hazehash, ?blurhash или ?thumbhash — только строка хеша, если хотите передать её сами: <VImage :src="photo" :hazehash="hash" :width="800" :height="600" />.
  • ?placeholder=blurhash,size — любой набор полей в одном импорте, например хеш вместе с реальными width и height. Поля: hazehash, blurhash, thumbhash, color, size, preview и aspect; см. Выбор того, что считать.
  • ?color, ?size, ?aspect, ?preview — по одному голому значению: '#c82828', { width, height }, 2, data URI крошечного PNG.
  • ?blurhash&components=6x4 — настройки только для этого изображения.

Размытие для целой папки через import.meta.glob ​

Когда применять. Много изображений в папке, а компонент выбирает нужное по имени файла или ключу.

ts
// vite.config.ts — как в предыдущем рецепте
vueImageKit({ generate: false })
vue
<script setup lang="ts">
const images = import.meta.glob('./assets/gallery/*.jpg', { import: 'default', eager: true })
const placeholders = import.meta.glob('./assets/gallery/*.jpg', {
  query: '?placeholder',
  import: 'default',
  eager: true,
})

const names = ['sunrise', 'forest', 'lake']
const path = (name: string) => `./assets/gallery/${name}.jpg`
</script>

<template>
  <VImage
    v-for="name in names"
    :key="name"
    :src="images[path(name)]"
    v-bind="placeholders[path(name)]"
    :alt="name"
  />
</template>

Что вы получите. Два объекта с одинаковыми ключами-путями: адреса изображений и их пропсы плейсхолдера. Достаточно положить файл в папку — дописывать импорт не нужно.

Размытие для изображений, путь которых приходит из данных ​

Когда применять. src не написан в шаблоне: он берётся из массива, CMS или JSON-файла (:src="category.image"). Один импорт на изображение здесь не сработает, поэтому передайте плагину папки целиком, и он построит манифест по URL.

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

export default defineConfig({
  plugins: [
    vue(),
    vueImageKit({
      generate: false,
      placeholders: { dirs: ['public/images'] },
    }),
  ],
})
ts
// main.ts
import { createApp } from 'vue'
import { VImageKitPlugin } from '@macrulez/vue-image-kit'
import placeholders from 'virtual:vue-image-kit/placeholders'
import App from './App.vue'

createApp(App).use(VImageKitPlugin, { placeholders }).mount('#app')
vue
<template>
  <VImage v-for="item in items" :key="item.id" :src="item.image" :alt="item.title" />
</template>

Что вы получите. Каждое изображение из public/images попадает в манифест с HazeHash (BlurHash, если hazehash не установлен), цветом и размером под тем URL, по которому оно отдаётся (/images/cat.jpg). <VImage> находит свою запись по точному src, так что в шаблоне пропсы не нужны совсем. Манифест пересобирается при изменении файла в папке и кешируется между сборками.

Что важно знать.

  • Файл из публичной папки записывается по пути от неё. Папке вне её нужен urlPrefix — см. следующий рецепт.
  • Изображение, импортированное в компонент, после сборки получает хешированный URL, и манифест его не найдёт. Для таких используйте импорт плейсхолдера.

Папки, которые отдаются с другого URL ​

Когда применять. Изображения хранятся в репозитории, но отдаются откуда-то ещё — с CDN, из маршрута загрузок, с префиксом, который добавляет прокси.

ts
vueImageKit({
  generate: false,
  placeholders: {
    dirs: ['public/images', { dir: 'content/photos', urlPrefix: 'https://cdn.example.com/photos' }],
  },
})

Что вы получите. Файлы из content/photos записываются как https://cdn.example.com/photos/<путь> — именно такой src используется в шаблоне, который ссылается на CDN. Без urlPrefix папка вне public/ записывается по пути в проекте (/content/photos/a.jpg), который совпадает только с URL dev-сервера, и сборка об этом предупредит.

Удалённые и CDN-изображения в манифесте ​

Когда применять. Изображения лежат на другом хосте и в репозитории их нет.

ts
vueImageKit({
  generate: false,
  placeholders: {
    urls: ['https://res.cloudinary.com/demo/image/upload/sample.jpg'],
    timeout: 20000,
  },
})

Что вы получите. Плагин скачивает каждый URL во время сборки и добавляет плейсхолдер в манифест под этим же адресом. URL распознаваемого CDN запрашивается в уменьшенной версии, а размер читается из начала оригинала. Результаты кешируются, поэтому повторная сборка не качает заново; refreshRemote: true заставит скачать снова.

Другой вид плейсхолдера и настройка ​

Когда применять. HazeHash по умолчанию не подходит: нужен BlurHash или ThumbHash, только цвет для самого лёгкого результата или более длинный HazeHash (tuning.budget).

ts
vueImageKit({
  generate: false,
  placeholders: {
    dirs: ['public/images'],
    mode: 'thumbhash',
    tuning: { components: [6, 4], sample: 128, color: 'average' },
  },
})
  • mode — 'hazehash' (по умолчанию, если установлен пакет hazehash), 'blurhash' (по умолчанию в остальных случаях), 'thumbhash' или 'color'. Он же задаёт, что вернёт импорт ?placeholder без значения.
  • tuning.components — число компонент BlurHash по каждой оси, по умолчанию [4, 3]. Больше компонент — больше деталей и длиннее строка.
  • tuning.sample — размер в пикселях, до которого изображение уменьшается перед расчётом хеша.
  • tuning.color — 'dominant' (самый частый цвет) или 'average'.

Эти настройки применяются ко всем плейсхолдерам, которые считает плагин, в том числе к импортам ?placeholder. При их смене кеш сбрасывается. См. Настройка хешей.

Адаптивные варианты для папки ​

Когда применять. Нужны заранее сделанные файлы WebP/AVIF нескольких ширин и srcset, построенный по ним.

ts
vueImageKit({
  input: './src/images',
  output: './public/images',
  widths: [400, 800, 1200],
  formats: ['webp', 'avif', 'jpg'],
  manifest: './src/assets/images.ts',
})
vue
<script setup lang="ts">
import { images } from './assets/images'

const hero = images.find((image) => image.name === 'hero')
</script>

<template>
  <VImage v-if="hero" :image="hero" alt="Hero" />
</template>

Что вы получите. При каждой сборке плагин уменьшает каждое изображение из input в output и пишет рядом с вашим кодом TypeScript-манифест: массив images с записью на каждый исходный файл, где name — имя файла без расширения. В vite dev обработка инкрементальная: при изменении одного файла пересобирается только он. Принимаются все опции команды generate.

Варианты для одного изображения ​

Когда применять. Варианты нужны одному hero-изображению, а обработка всей папки не нужна.

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

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

Что вы получите. ?vik уменьшает именно этот файл в output и возвращает полные метаданные — адреса, srcset, размер, BlurHash и ThumbHash, — которые проп image принимает как есть. Варианты попадают в папку output плагина (по умолчанию ./public/images). Добавьте generate: false, если остальную часть input обрабатывать не нужно:

ts
vueImageKit({ generate: false, output: './public/images', widths: [400, 800, 1200] })

Варианты и манифест папок вместе ​

Когда применять. Для одних изображений нужны сгенерированные варианты, другие отдаются как есть и требуют только размытия.

ts
vueImageKit({
  input: './src/images',
  output: './public/images',
  widths: [400, 800, 1200],
  placeholders: { dirs: ['public/uploads'] },
})

Что вы получите. Эти две задачи независимы: пакетная обработка берёт input, а виртуальный манифест покрывает public/uploads. Манифест регистрируется так же, как в рецепте про папки выше.

Варианты по запросу в dev ​

Когда применять. Во время разработки не хочется шага сборки: изображения уменьшаются по запросу и кешируются.

ts
vueImageKit({ generate: false, dev: { onDemand: true } })
html
<img src="/_vik/image?src=/photos/cat.jpg&w=800&format=webp" />

Что вы получите. Обработчик на /_vik/image, который работает только в vite dev. Для продакшена без CDN используйте свой сервер.