Изображения: форматы webp, avif и автоконвертация

Оптимизация изображений является одной из ключевых задач фронтенд-сборки. Размер графических ресурсов напрямую влияет на скорость загрузки, показатель Largest Contentful Paint (LCP), время рендеринга и потребление трафика. В Webpack обработка изображений давно вышла за пределы простого копирования файлов в директорию dist. Современные проекты используют автоматическую генерацию оптимизированных форматов, сжатие, адаптивные размеры и конвертацию изображений во время сборки.

Наиболее востребованными форматами для веба стали WebP и AVIF. Оба обеспечивают значительно меньший размер файлов по сравнению с JPEG и PNG при сохранении качества изображения.


Формат WebP

Формат WebP разработан компанией Google как универсальная замена JPEG, PNG и GIF.

Возможности WebP

  • сжатие с потерями и без потерь;
  • поддержка прозрачности;
  • поддержка анимации;
  • уменьшение размера изображений на 25–40%;
  • хорошая поддержка браузерами.

Пример уменьшения размера

Формат Размер
JPEG 420 KB
WebP 240 KB

При большом количестве изображений экономия становится существенной.


Формат AVIF

AVIF считается следующим поколением оптимизированной веб-графики.

Особенности AVIF

  • более эффективное сжатие;
  • лучшее качество при меньшем размере;
  • HDR и расширенная цветовая глубина;
  • поддержка прозрачности;
  • поддержка анимации.

Сравнение размеров

Формат Размер
JPEG 420 KB
WebP 240 KB
AVIF 160 KB

Поддержка браузерами

WebP

Поддерживается практически всеми современными браузерами:

  • Chrome
  • Firefox
  • Edge
  • Safari
  • Opera

AVIF

Поддержка AVIF появилась позже:

  • Chrome
  • Firefox
  • Edge
  • Safari 16+
  • Android Browser

В старых браузерах AVIF может отсутствовать, поэтому обычно используется fallback.


Обработка изображений в Webpack 5

Webpack 5 содержит встроенные Asset Modules, которые заменили:

  • file-loader
  • url-loader
  • raw-loader

Для изображений чаще всего используется:

type: 'asset/resource'

Базовая настройка изображений

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|jpeg|gif|svg)$/i,
        type: 'asset/resource',
      },
    ],
  },
}

Webpack будет:

  • копировать изображения в dist;
  • генерировать хеш;
  • обновлять пути автоматически.

Подключение WebP и AVIF

Современные проекты обычно расширяют правило:

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|jpeg|webp|avif)$/i,
        type: 'asset/resource',
      },
    ],
  },
}

Теперь Webpack умеет импортировать:

import image from './photo.avif'

Настройка имени файлов

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|jpeg|webp|avif)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'images/[name].[contenthash][ext]',
        },
      },
    ],
  },
}

Автоматическая оптимизация изображений

Сам Webpack не выполняет сжатие изображений. Для этого используются дополнительные плагины.

Наиболее популярный инструмент:

  • image-minimizer-webpack-plugin

Установка image-minimizer-webpack-plugin

npm install image-minimizer-webpack-plugin imagemin --save-dev

Дополнительно устанавливаются кодеки:

npm install imagemin-webp imagemin-avif --save-dev

Базовая настройка оптимизации

const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin')

module.exports = {
  optimization: {
    minimizer: [
      new ImageMinimizerPlugin({
        minimizer: {
          implementation:
            ImageMinimizerPlugin.imageminMinify,
          options: {
            plugins: [
              ['imagemin-mozjpeg', {
                quality: 75,
              }],
              ['imagemin-pngquant', {
                quality: [0.6, 0.8],
              }],
            ],
          },
        },
      }),
    ],
  },
}

Автоматическая генерация WebP

Webpack может автоматически создавать WebP-версии файлов во время сборки.

Установка

npm install imagemin-webp --save-dev

Генерация WebP через generator

const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin')

module.exports = {
  optimization: {
    minimizer: [
      new ImageMinimizerPlugin({
        generator: [
          {
            preset: 'webp',
            implementation:
              ImageMinimizerPlugin.imageminGenerate,
            filename: '[path][name].webp',
            options: {
              plugins: [
                ['imagemin-webp', {
                  quality: 80,
                }],
              ],
            },
          },
        ],
      }),
    ],
  },
}

Что происходит при сборке

Исходный файл:

photo.jpg

После сборки:

photo.jpg
photo.webp

Webpack создаёт дополнительный оптимизированный формат автоматически.


Генерация AVIF

Установка

npm install imagemin-avif --save-dev

Настройка AVIF

const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin')

module.exports = {
  optimization: {
    minimizer: [
      new ImageMinimizerPlugin({
        generator: [
          {
            preset: 'avif',
            implementation:
              ImageMinimizerPlugin.imageminGenerate,
            filename: '[path][name].avif',
            options: {
              plugins: [
                ['imagemin-avif', {
                  quality: 50,
                }],
              ],
            },
          },
        ],
      }),
    ],
  },
}

Одновременная генерация WebP и AVIF

Часто используются сразу оба формата.

const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin')

module.exports = {
  optimization: {
    minimizer: [
      new ImageMinimizerPlugin({
        generator: [
          {
            preset: 'webp',
            implementation:
              ImageMinimizerPlugin.imageminGenerate,
            filename: '[path][name].webp',
            options: {
              plugins: [
                ['imagemin-webp', {
                  quality: 80,
                }],
              ],
            },
          },
          {
            preset: 'avif',
            implementation:
              ImageMinimizerPlugin.imageminGenerate,
            filename: '[path][name].avif',
            options: {
              plugins: [
                ['imagemin-avif', {
                  quality: 50,
                }],
              ],
            },
          },
        ],
      }),
    ],
  },
}

Использование <picture>

Для корректной работы fallback обычно применяется тег <picture>.

<picture>
  <source
    srcset="photo.avif"
    type="image/avif"
  />

  <source
    srcset="photo.webp"
    type="image/webp"
  />

  <img
    src="photo.jpg"
    alt="Photo"
  />
</picture>

Приоритет загрузки

Браузер выбирает:

  1. AVIF;
  2. WebP;
  3. JPEG/PNG fallback.

Импорт изображений в JavaScript

import photoJpg from './photo.jpg'
import photoWebp from './photo.webp'
import photoAvif from './photo.avif'

Генерация HTML через шаблоны

В проектах с Pug, Handlebars или HTMLWebpackPlugin часто используются динамические пути:

<img src="<%= photo %>" />

Автоматическое подключение WebP

Некоторые плагины умеют автоматически заменять изображения в HTML.

Например:

  • html-webpack-plugin
  • image-minimizer-webpack-plugin
  • responsive-loader

Использование responsive-loader

Плагин умеет:

  • создавать несколько размеров;
  • генерировать WebP;
  • генерировать srcset;
  • автоматически оптимизировать изображения.

Установка responsive-loader

npm install responsive-loader sharp --save-dev

Настройка responsive-loader

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|jpeg)$/i,
        use: [
          {
            loader: 'responsive-loader',
            options: {
              adapter: require('responsive-loader/sharp'),
              sizes: [320, 640, 1280],
              format: 'webp',
            },
          },
        ],
      },
    ],
  },
}

Результат генерации

Webpack создаёт:

image-320.webp
image-640.webp
image-1280.webp

Использование srcset

<img
  srcset="
    image-320.webp 320w,
    image-640.webp 640w,
    image-1280.webp 1280w
  "
  sizes="(max-width: 768px) 100vw, 50vw"
  src="image-640.webp"
  alt=""
/>

Использование Sharp

Библиотека Sharp является одним из самых быстрых инструментов обработки изображений в Node.js.

Возможности Sharp

  • изменение размеров;
  • конвертация;
  • сжатие;
  • генерация AVIF;
  • генерация WebP;
  • кадрирование;
  • поворот;
  • пакетная обработка.

Генерация изображений через Sharp вручную

const sharp = require('sharp')

sharp('input.jpg')
  .webp({ quality: 80 })
  .toFile('output.webp')

Генерация AVIF через Sharp

const sharp = require('sharp')

sharp('input.jpg')
  .avif({ quality: 50 })
  .toFile('output.avif')

Использование Sharp внутри Webpack

Многие плагины используют Sharp как backend:

  • responsive-loader
  • image-minimizer-webpack-plugin
  • image-webpack-loader

image-webpack-loader

Ещё один популярный загрузчик:

npm install image-webpack-loader --save-dev

Настройка image-webpack-loader

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'file-loader',
          },
          {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 75,
              },

              webp: {
                quality: 80,
              },
            },
          },
        ],
      },
    ],
  },
}

Разница между оптимизацией и генерацией

Оптимизация

Сжатие существующего файла:

photo.jpg → photo.jpg

Размер уменьшается.

Генерация

Создание нового формата:

photo.jpg → photo.webp
photo.jpg → photo.avif

Inline-изображения и WebP

Небольшие изображения можно встраивать как Data URL.

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|webp)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024,
          },
        },
      },
    ],
  },
}

Проблемы AVIF

Несмотря на преимущества, AVIF имеет особенности.

Более медленное кодирование

Сборка проекта может стать значительно медленнее.

Высокая нагрузка на CPU

Особенно заметно при CI/CD.

Иногда хуже для мелких изображений

Для маленьких иконок WebP может оказаться эффективнее.


Когда использовать WebP

WebP хорошо подходит для:

  • каталогов;
  • блогов;
  • интернет-магазинов;
  • интерфейсов;
  • адаптивных изображений;
  • анимаций.

Когда использовать AVIF

AVIF особенно эффективен для:

  • больших баннеров;
  • hero-image;
  • фотографий;
  • полноэкранных изображений;
  • контента с высоким разрешением.

Комбинированная стратегия

Наиболее распространённый современный подход:

Формат Назначение
AVIF основной
WebP fallback
JPEG/PNG поддержка старых браузеров

Производительность Lighthouse

Использование AVIF и WebP положительно влияет на:

  • Performance;
  • LCP;
  • Total Blocking Time;
  • Speed Index.

Lighthouse напрямую рекомендует современные форматы изображений.


Кэширование изображений

Хеширование особенно важно для изображений.

generator: {
  filename: 'images/[name].[contenthash][ext]'
}

Преимущества:

  • долгий browser cache;
  • отсутствие конфликтов версий;
  • автоматическая инвалидизация.

Public Path для CDN

output: {
  publicPath: 'https://cdn.example.com/',
}

Изображения будут загружаться через CDN.


Lazy Loading изображений

Современные браузеры поддерживают:

<img
  src="photo.webp"
  loading="lazy"
  alt=""
>

Предзагрузка hero-image

<link
  rel="preload"
  as="image"
  href="/images/hero.avif"
>

Оптимизация favicon

Даже favicon можно хранить в WebP или AVIF, если браузер поддерживает формат.


SVG и WebP

SVG обычно не конвертируют в WebP или AVIF, поскольку:

  • SVG уже является векторным;
  • часто имеет меньший размер;
  • масштабируется без потери качества.

Типичная структура production-сборки

src/
  assets/
    images/

dist/
  images/
    photo.webp
    photo.avif
    photo.jpg

Production-конфигурация

const ImageMinimizerPlugin =
  require('image-minimizer-webpack-plugin')

module.exports = {
  mode: 'production',

  module: {
    rules: [
      {
        test: /\.(png|jpg|jpeg|webp|avif)$/i,
        type: 'asset/resource',
        generator: {
          filename:
            'images/[name].[contenthash][ext]',
        },
      },
    ],
  },

  optimization: {
    minimizer: [
      '...',

      new ImageMinimizerPlugin({
        minimizer: {
          implementation:
            ImageMinimizerPlugin.imageminMinify,

          options: {
            plugins: [
              ['imagemin-mozjpeg', {
                quality: 75,
              }],
            ],
          },
        },

        generator: [
          {
            preset: 'webp',

            implementation:
              ImageMinimizerPlugin.imageminGenerate,

            filename:
              '[path][name][ext].webp',

            options: {
              plugins: [
                ['imagemin-webp', {
                  quality: 80,
                }],
              ],
            },
          },

          {
            preset: 'avif',

            implementation:
              ImageMinimizerPlugin.imageminGenerate,

            filename:
              '[path][name][ext].avif',

            options: {
              plugins: [
                ['imagemin-avif', {
                  quality: 50,
                }],
              ],
            },
          },
        ],
      }),
    ],
  },
}