Перейти к содержанию

Автоматизация

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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/'));
};

При таком процессе не будет нанесен ущерб производственной среде, если кто-то из участников проекта случайно добавит в каталог с исходными изображениями фотографию, закодированную как массивный truecolor PNG. Независимо от кодировки исходного изображения, эта задача позволит получить эффективный WebP и надежный прогрессивный JPEG, причем со степенью сжатия, которую можно легко настроить "на лету". Разумеется, при этом исходные файлы изображений будут сохранены в среде разработки проекта, а значит, эти настройки можно будет изменить в любой момент, причем перезаписываться будет только автоматический вывод.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 КБ.

1
2
3
4
5
6
7
8
9
[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 обычно является простым ручным процессом, поскольку атрибут действительно только собирает информацию о конфигурации, которую вы уже выполнили при генерации исходных текстов. В приведенных выше задачах мы определили имена файлов и информацию о ширине, которой будет следовать наш атрибут:

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<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 и выбирают атрибут <source> элемента `srcset в качестве списка кандидатов на изображение. Браузеры, не распознающие image/webp в качестве допустимого медиатипа, проигнорируют этот <source> и вместо него используют атрибут srcset внутреннего элемента <img>.

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<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 и sizes предназначен для оптимизации запросов к графическим активам. Несмотря на то, что их не стоит использовать в производстве, значение sizes по умолчанию, равное 100vw, вполне оправдано при работе над макетом страницы в локальной среде разработки. После того как стили верстки будут созданы, запуск respImageLint предоставит вам атрибуты sizes, которые вы сможете скопировать и вставить в свою разметку с уровнем детализации, намного превышающим тот, что вы напишете вручную:

Отчет по отзывчивым изображениям с предлагаемыми размерами.

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

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

Источник — Automating compression and encoding

Комментарии