Автоматизация сжатия и кодирования

Сделайте создание высокопроизводительных источников изображений неотъемлемой частью вашего процесса разработки.

Все синтаксисы этого руководства — от кодирования данных изображения до информационно насыщенной разметки, обеспечивающей работу адаптивных изображений, представляют собой методы общения машин друг с другом. Вы открыли для себя несколько способов, с помощью которых клиентский браузер может сообщать о своих потребностях серверу, а сервер отвечать на них. Разметка адаптивных изображений (в частности, атрибуты srcset и sizes) позволяет описать шокирующий объём информации в относительно небольшом количестве символов. Хорошо это или плохо, но такая краткость обусловлена замыслом: если сделать эти синтаксисы менее лаконичными и тем самым облегчить их разбор для разработчиков, это может усложнить их разбор для браузера. Чем больше сложностей добавляется к строке, тем больше вероятность появления ошибок или непреднамеренных различий в поведении браузера.

Однако та же самая причина, из-за которой эти темы кажутся такими пугающими, может дать вам и решение: синтаксис, легко читаемый машинами, — это синтаксис, который легче пишется ими. Вы почти наверняка сталкивались с многочисленными примерами автоматического кодирования и сжатия изображений, будучи пользователем Интернета. Любое изображение, загруженное через платформы социальных сетей, системы управления содержимым (CMS) и даже почтовые клиенты, почти неизменно проходит через систему, которая меняет размер изображения, кодирует и сжимает его.

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

Вот две основные проблемы, связанные с автоматизацией производительности изображений: управление созданием изображений — их кодированием, сжатием и альтернативными источниками, которые вы будете использовать для атрибута srcset; и создание разметки, ориентированной на пользователя. В этом разделе вы узнаете о нескольких распространённых подходах к управлению изображениями в рамках современного рабочего процесса, будь то автоматизированная фаза разработки, фреймворк или CMS, на которой работает ваш сайт, или почти полностью абстрагированная от него специальная сеть доставки содержимого.

Автоматизация сжатия и кодирования

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

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

При выборе кодировки для каталога фотографий формат AVIF является явным победителем по качеству и размеру передачи, но имеет ограниченную поддержку, WebP обеспечивает оптимизированный, современный запасной вариант, а JPEG является наиболее надёжным по умолчанию. Альтернативные размеры, которые мы должны получить для изображений, предназначенных для боковой панели в макете страницы, будут сильно отличаться от изображений, которые должны занимать всю область видимости браузера в наибольших точках останова. Настройки сжатия потребуют внимания к размытию и артефактам сжатия в нескольких результирующих файлах — меньше возможностей для вырезания каждого возможного байта из каждого изображения в обмен на более гибкий и надёжный рабочий процесс. В общем, вы будете следовать тому же процессу принятия решений, который поняли из этого руководства.

Что касается самой обработки изображений, то для этого существует огромное количество библиотек с открытым исходным кодом, которые предлагают методы преобразования, модификации и редактирования изображений в пакетном режиме, конкурируя между собой по скорости, эффективности и надёжности. Эти библиотеки позволят вам применять параметры кодирования и сжатия к целым каталогам изображений сразу, без необходимости открывать программы для редактирования изображений, при этом сохраняя исходные изображения, если эти параметры нужно будет скорректировать на лету. Они предназначены для работы в различных контекстах, от локальной среды разработки до самого веб-сервера. Например, ориентированный на сжатие ImageMin для Node.js может быть расширен для конкретных приложений с помощью множества плагинов, а кроссплатформенный ImageMagick и основанный на Node.js Sharp поставляются с ошеломляющим количеством функций прямо из коробки.

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

Инструменты и рабочие процессы локальной разработки

Менеджеры задач и сборщики, такие как Grunt, Gulp или Webpack, могут быть использованы для оптимизации изображений наряду с другими рядовыми задачами, связанными с производительностью, такими как минификация CSS и JavaScript. Для примера рассмотрим относительно простой случай: каталог в вашем проекте содержит десяток фотографий, предназначенных для использования на публичном сайте.

Сперва вам нужно обеспечить последовательное и эффективное кодирование этих изображений. Как вы узнали из предыдущих разделов, WebP — это эффективный формат по умолчанию для фотографий, с точки зрения качества и размера файла. WebP хорошо поддерживается, но не повсеместно, поэтому вам также нужно предусмотреть резервный вариант в виде прогрессивного JPEG. Затем, чтобы использовать атрибут srcset для эффективной доставки этих ресурсов, вам потребуется создать несколько альтернативных размеров для каждого формата.

Если при работе с программами для редактирования изображений это занятие было бы повторяющимся и отнимало много времени, то менеджеры задач вроде Gulp предназначены для автоматизации именно такого рода повторений. Плагин gulp-responsive, использующий Sharp, — один из многих вариантов, которые работают по схожей схеме: собирают все файлы в исходном каталоге, кодируют и сжимают их на основе того же стандартного «качества», о котором мы уже говорили. Полученные файлы сохраняются по указанному вами пути, на них можно ссылаться в атрибутах src элементов <img>, при этом исходные файлы остаются нетронутыми.

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.webp = function() {
  return src('./src-img/*')
    .pipe(respimg({
      '*': [{
        quality: 70,
        format: ['webp', 'jpeg'],
        progressive: true
      }]
  }))
  .pipe(dest('./img/'));
}

Благодаря такому процессу производственная среда не пострадает, если кто-то из участников проекта случайно добавит в каталог с исходными изображениями фотографию, кодированную как полноцветный PNG. Независимо от формата исходного изображения, задача Gulp создаст эффективный WebP и надёжный прогрессивный JPEG, причём со степенью сжатия, которую можно легко настроить «на лету». Конечно, этот процесс также гарантирует, что ваши исходные файлы изображений будут сохранены в среде разработки проекта, а это значит, что эти настройки можно будет изменить в любое время, автоматически перезаписав выходные файлы.

Чтобы вывести несколько файлов, вы передаёте несколько объектов конфигурации — все они одинаковые, за исключением ключа width и значения в пикселях:

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.default = function() {
  return src('./src-img/*')
    .pipe(respimg({
      '*': [{
        width: 1000,
        format: ['jpeg', 'webp'],
        progressive: true,
        rename: { suffix: '-1000' }
        },
        {
        width: 800,
        format: ['jpeg', 'webp'],
        progressive: true,
        rename: { suffix: '-800' }
        },
        {
        width: 400,
        format: ['jpeg', 'webp'],
        progressive: true,
        rename: { suffix: '-400' },
    }]
    })
  )
  .pipe(dest('./img/'));
}

В приведённом примере исходное изображение (monarch.png) занимало более 3.3 Мб. Самый большой файл, созданный этой задачей (monarch-1000.jpeg), имеет размер около 150 Кб. Самый маленький, monarch-400.webp, имеет размер всего 32 Кб.

[10:30:54] Starting 'default'...
[10:30:54] gulp-responsive: monarch.png -> monarch-400.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-800.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-400.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-800.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.webp
[10:30:54] gulp-responsive: Created 6 images (matched 1 of 1 image)
[10:30:54] Finished 'default' after 374 ms

Конечно, вы захотите внимательно изучить результаты на предмет видимых артефактов сжатия или, возможно, увеличить сжатие для дополнительной экономии. Поскольку задача недеструктивная, эти настройки можно легко поменять.

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

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

Установка атрибутов srcset обычно представляет собой простой ручной процесс, так как атрибут действительно только собирает информацию о конфигурации, которую вы уже выполнили при создании исходников. В приведённой выше задаче мы определили имена файлов и информацию о ширине, которой будет соответствовать наш атрибут:

srcset="filename-1000.jpg 1000w, 
        filename-800.jpg 800w, 
        filename-400.jpg 400w"

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

Как вы узнали в предыдущем разделе, вам понадобится использовать элемент <picture> для удобной обработки шаблона WebP или JPEG. В этом случае вы будете применять атрибут type в сочетании с srcset.

<picture>
  <source
    type="image/webp" 
    srcset="filename-1000.webp 1000w, 
            filename-800.webp 800w,
            filename-400.webp 400w">
  <img
    srcset="filename-1000.jpg 1000w, 
            filename-800.jpg 800w, 
            filename-400.jpg 400w" 
    sizes="…" alt="…">
</picture>

Как вы уже поняли, браузеры, поддерживающие WebP, распознают содержимое type и выбирают атрибут srcset элемента <source> в качестве списка возможных изображений. Браузеры, не принимающие image/webp как допустимый тип носителя, будут игнорировать этот <source> и вместо него использовать атрибут srcset вложенного элемента <img>.

Есть ещё одно соображение с точки зрения поддержки браузерами — тем браузерам, которые не поддерживают адаптивную разметку изображений, всё равно потребуется запасной вариант, иначе мы рискуем получить битое изображение, особенно в контексте старых браузеров. Поскольку <picture>, <source> и srcset в этих браузерах игнорируются, мы желаем указать источник по умолчанию в атрибуте src вложенного элемента <img>.

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

<picture> <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w"> <img src="filename-1000.jpg" srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…"> </picture>

С атрибутом sizes дело иметь несколько сложнее. Как вы уже поняли, sizes обязательно зависит от контекста — вы не можете установить атрибут, не зная, сколько места изображение должно занимать в отображаемом макете. Чтобы запросы были максимально эффективными, точный атрибут sizes должен присутствовать в нашей разметке в момент запроса конечного пользователя, задолго до того, как будут запрошены стили, управляющие макетом страницы. Полное отсутствие атрибута sizes не только является нарушением спецификации HTML, но и приводит к поведению по умолчанию, эквивалентному sizes="100vw" — информированию браузера о том, что данное изображение ограничено только областью просмотра, в результате чего выбираются самые большие из возможных источников.

Как и в случае с любой особенно трудоёмкой задачей веб-разработки, был создан ряд инструментов, позволяющих абстрагироваться от процесса ручного написания атрибутов sizes. respImageLint — это абсолютно необходимый фрагмент кода, предназначенный для проверки точности атрибутов sizes и предложений по их улучшению. Он работает как закладки — инструмент, который вы запускаете в браузере, указывая при этом на полностью готовую страницу, содержащую элементы изображения. В контексте, когда браузер имеет полное представление о макете страницы, он также будет иметь почти попиксельно точное представление о пространстве, которое изображение должно занимать в этом макете при любом возможном размере области просмотра.

Инструмент для проверки атрибутов sizes, безусловно, полезен, но ещё более ценен он в качестве инструмента для их массовой генерации. Как вы знаете, синтаксис srcset и size предназначен для визуальной оптимизации запросов к ресурсам изображений. Хотя это и не то, что когда-либо следует использовать на рабочем сайте, значение sizes по умолчанию, равное 100vw, вполне разумно при работе над макетом страницы в локальной среде разработки. После того как стили макета будут созданы, запуск respImageLint предоставит вам атрибуты sizes, которые вы можете скопировать и вставить в свою разметку, на уровне детализации, гораздо более высоком, чем тот, который написан вручную:

Хотя запросы изображений, инициированные разметкой, отображаемой на сервере, происходят слишком быстро, чтобы JavaScript мог сгенерировать атрибут sizes на стороне клиента, те же рассуждения не применимы, если эти запросы инициируются на стороне клиента. Например, проект Lazysizes позволяет вам полностью отложить запросы изображений до тех пор, пока не будет создан макет, позволяя JavaScript генерировать значения наших sizes — огромное удобство для вас и гарантия максимально эффективных запросов для ваших пользователей. Однако имейте в виду, что этот подход означает принесение в жертву надёжности разметки, отображаемой сервером, и оптимизацию скорости, встроенную в браузеры, а инициирование этих запросов только после того, как страница была отображена, окажет огромное негативное влияние на ваш показатель LCP.

Конечно, если вы уже зависите от клиентского фреймворка, такого как React или Vue, то уже берёте на себя этот долг — и в таких случаях использование Lazysizes означает, что ваши атрибуты sizes могут быть почти полностью абстрагированы. Что ещё лучше: по мере того, как sizes="auto" для лениво загружаемых изображений будет набирать популярность и широко распространяться, Lazysizes фактически станет полифилом для этого нового стандартизированного поведения браузера.

Автор: Мэт Маркиз
Последнее изменение: 20.05.2024