Компонент обработки изображений воплощает в себе лучшие практики повышения производительности и предоставляет готовое решение для оптимизации изображений.
Изображения являются распространенным источником узких мест в производительности веб-приложений и ключевой областью для оптимизации. Неоптимизированные изображения приводят к раздуванию страницы и составляют более 70% от общего веса страницы в байтах на 90 -м процентиле. Существует множество способов оптимизации изображений, требующих интеллектуального «компонента для работы с изображениями» с встроенными по умолчанию решениями для повышения производительности.
Команда Aurora работала с Next.js над созданием одного из таких компонентов . Целью было создание оптимизированного шаблона изображения, который веб-разработчики могли бы дополнительно настраивать. Этот компонент служит хорошей моделью и устанавливает стандарт для создания компонентов изображений в других фреймворках, системах управления контентом (CMS) и технологических стеках. Мы сотрудничали над аналогичным компонентом для Nuxt.js и работаем с Angular над оптимизацией изображений в будущих версиях. В этом посте рассказывается о том, как мы проектировали компонент изображений для Next.js и какие уроки мы извлекли в процессе работы.

Проблемы и возможности оптимизации изображений
Изображения влияют не только на производительность, но и на бизнес. Количество изображений на странице оказалось вторым по значимости фактором, определяющим конверсию пользователей, посещающих веб-сайты. В сессиях, в которых пользователи совершали конверсию, изображений было на 38% меньше, чем в сессиях, где конверсия не происходила. В рамках аудита передовых практик Lighthouse перечисляет множество возможностей для оптимизации изображений и улучшения показателей веб-сайта . Ниже перечислены некоторые распространенные области, где изображения могут влиять на ключевые показатели веб-сайта и пользовательский опыт.
Изображения неправильного размера вредят CLS
Изображения, отображаемые без указания их размера, могут вызывать нестабильность макета и приводить к высокому показателю кумулятивного смещения макета ( CLS ). Установка атрибутов width и height для элементов <img> может помочь предотвратить смещение макета. Например:
<img src="flower.jpg" width="360" height="240">
Ширина и высота должны быть заданы таким образом, чтобы соотношение сторон отображаемого изображения было близко к его естественному соотношению сторон. Значительное различие в соотношении сторон может привести к искажению изображения. Относительно новое свойство, позволяющее задавать соотношение сторон в CSS, может помочь адаптировать размер изображений под индивидуальные потребности пользователя, предотвращая при этом искажение размеров (CLS).
Большие изображения могут навредить LCP.
Чем больше размер файла изображения, тем дольше будет длиться его загрузка. Большое изображение может быть «главным» изображением страницы или наиболее значимым элементом в области просмотра, ответственным за запуск процесса отрисовки самого большого контента ( LCP ). Изображение, являющееся частью критически важного контента и требующее длительного времени для загрузки, задержит запуск LCP.
Во многих случаях разработчики могут уменьшить размер изображений за счет лучшего сжатия и использования адаптивных изображений. Атрибуты srcset и sizes элемента <img> помогают предоставлять файлы изображений разных размеров. Браузер затем может выбрать подходящий вариант в зависимости от размера и разрешения экрана.
Некачественное сжатие изображений может навредить LCP.
Современные форматы изображений, такие как AVIF или WebP, обеспечивают лучшее сжатие, чем широко используемые форматы JPEG и PNG. Лучшее сжатие в некоторых случаях уменьшает размер файла на 25–50% при том же качестве изображения. Это уменьшение приводит к более быстрой загрузке с меньшим потреблением данных. Приложение должно предоставлять современные форматы изображений браузерам, поддерживающим эти форматы.
Загрузка ненужных изображений вредит LCP.
Изображения, находящиеся ниже видимой области или за пределами видимой области, не отображаются пользователю при загрузке страницы. Их можно отложить, чтобы они не влияли на LCP (клиентский контекст загрузки) и не задерживали его. Ленивая загрузка может использоваться для загрузки таких изображений позже, по мере того, как пользователь прокручивает страницу в сторону них.
Проблемы оптимизации
Команды могут оценить снижение производительности из-за перечисленных выше проблем и внедрить лучшие практики для их преодоления. Однако на практике это часто не происходит, и неэффективные изображения продолжают замедлять работу веб-сайта. Возможные причины этого включают:
- Приоритеты : Веб-разработчики, как правило, сосредотачиваются на коде, JavaScript и оптимизации данных. Поэтому они могут не знать о проблемах с изображениями или о том, как их оптимизировать. Изображения, созданные дизайнерами или загруженные пользователями, могут не занимать высокого места в списке приоритетов.
- Готовое решение : Даже если разработчики знакомы с нюансами оптимизации изображений, отсутствие универсального готового решения для их фреймворка или технологического стека может стать сдерживающим фактором.
- Динамические изображения : Помимо статических изображений, являющихся частью приложения, динамические изображения загружаются пользователями или поступают из внешних баз данных или CMS. Определение размера таких изображений может быть сложной задачей, если источник изображения является динамическим.
- Перегрузка разметкой : Решения для указания размера изображения или
srcsetдля разных размеров требуют дополнительной разметки для каждого изображения, что может быть утомительно. Атрибутsrcsetбыл введен в 2014 году, но сегодня используется только 26,5% веб-сайтов. При использованииsrcsetразработчикам приходится создавать изображения разных размеров. Такие инструменты, как just-gimme-an-img, могут помочь, но их приходится использовать вручную для каждого изображения. - Поддержка браузеров : Современные форматы изображений, такие как AVIF и WebP, создают файлы изображений меньшего размера, но требуют специальной обработки в браузерах, которые их не поддерживают. Разработчикам приходится использовать такие стратегии, как согласование содержимого или элемент
<picture, чтобы изображения отображались во всех браузерах. - Сложности, связанные с отложенной загрузкой : существует множество методов и библиотек для реализации отложенной загрузки изображений, находящихся ниже видимой части экрана. Выбор наилучшего варианта может быть непростой задачей. Разработчики также могут не знать оптимального расстояния от «прозрачной части» экрана для загрузки отложенных изображений. Различные размеры области просмотра на разных устройствах могут еще больше усложнить ситуацию.
- Изменение ландшафта : Поскольку браузеры начинают поддерживать новые функции HTML или CSS для повышения производительности, разработчикам может быть сложно оценить каждую из них. Например, Chrome представляет функцию Fetch Priority в качестве пробной версии Origin Trial . Ее можно использовать для повышения приоритета определенных изображений на странице. В целом, разработчикам было бы проще, если бы такие улучшения оценивались и внедрялись на уровне компонентов.
Компонент изображения как решение
Возможности оптимизации изображений и сложности их индивидуальной реализации для каждого приложения привели нас к идее компонента изображения. Компонент изображения может инкапсулировать и обеспечивать соблюдение лучших практик. Заменив элемент <img> компонентом изображения, разработчики могут лучше решить проблемы с производительностью обработки изображений.
В течение последнего года мы работали с фреймворком Next.js над разработкой и реализацией его компонента Image . Его можно использовать в качестве прямой замены существующих элементов <img> в приложениях Next.js следующим образом.
// Before with <img> element:
function Logo() {
return <img src="/logo.jpg" alt="logo" height="200" width="100" />
}
// After with image component:
import Image from 'next/image'
function Logo() {
return <Image src="/logo.jpg" alt="logo" height="200" width="100" />
}
Этот компонент пытается решать проблемы, связанные с обработкой изображений, универсальным способом, используя богатый набор функций и принципов. Он также включает в себя опции, позволяющие разработчикам настраивать его под различные требования к обработке изображений.
Защита от смещений компоновки
Как обсуждалось ранее, изображения без указания размера вызывают смещения макета и способствуют возникновению CLS (Closed Listing Score). При использовании компонента Image в Next.js разработчики должны указывать размер изображения с помощью атрибутов width и height , чтобы предотвратить любые смещения макета. Если размер неизвестен, разработчики должны указать layout=fill , чтобы отображать изображение без указания размера, которое находится внутри контейнера с заданным размером. В качестве альтернативы можно использовать статический импорт изображений, чтобы получить размер фактического изображения на жестком диске во время сборки и включить его в изображение.
// Image component with width and height specified
<Image src="/logo.jpg" alt="logo" height="200" width="100" />
// Image component with layout specified
<Image src="/hero.jpg" layout="fill" objectFit="cover" alt="hero" />
// Image component with image import
import Image from 'next/image'
import logo from './logo.png'
function Logo() {
return <Image src={logo} alt="logo" />
}
Поскольку разработчики не могут использовать компонент Image без указания размера, такая конструкция гарантирует, что они уделят время учету размера изображений и предотвратят смещение макета.
Облегчение реагирования
Чтобы изображения были адаптивными для разных устройств, разработчикам необходимо задавать атрибуты srcset и sizes в элементе <img> . Мы хотели упростить этот процесс с помощью компонента Image. Мы разработали компонент Image для Next.js таким образом, чтобы значения атрибутов устанавливались только один раз для всего приложения. Мы применяем их ко всем экземплярам компонента Image в зависимости от режима компоновки. Мы разработали решение, состоящее из трех частей:
- Свойство
deviceSizes: это свойство можно использовать для однократной настройки точек останова на основе устройств, используемых пользователями приложения. Значения по умолчанию для точек останова указаны в файле конфигурации. - Свойство
imageSizes: Это также настраиваемое свойство, используемое для получения размеров изображений, соответствующих контрольным точкам размера устройства. - Атрибут
layoutв каждом изображении: Он используется для указания способа применения свойствdeviceSizesиimageSizesдля каждого изображения. Поддерживаемые значения для режима layout:fixed,fill,intrinsicиresponsive
Когда запрашивается изображение с режимами компоновки «адаптивный» или «заполнение» , Next.js определяет, какое изображение следует отобразить, исходя из размера устройства, запрашивающего страницу, и соответствующим образом устанавливает атрибуты srcset и sizes изображения.
Приведенное ниже сравнение демонстрирует, как режим компоновки может использоваться для управления размером изображения на разных экранах. Мы использовали демонстрационное изображение, представленное в документации Next.js, просмотренное на телефоне и стандартном ноутбуке.
| экран ноутбука | Экран телефона |
|---|---|
| Layout = Intrinsic: Уменьшается до ширины контейнера на экранах меньшего размера. Не увеличивается за пределы внутреннего размера изображения на экранах большего размера. Ширина контейнера составляет 100%. | |
![]() | ![]() |
| Макет = Фиксированный: Изображение не адаптивное. Ширина и высота фиксированы, как в ` | |
![]() | ![]() |
| Макет = Адаптивный: Масштабирование в зависимости от ширины контейнера на разных экранах с сохранением пропорций. | |
![]() | ![]() |
Layout = Fill: Ширина и высота растягиваются, чтобы заполнить родительский контейнер. (В этом примере ширина родительского элемента <div> установлена на 300*500) | |
![]() | ![]() |
Предусмотреть встроенную отложенную загрузку
Компонент Image по умолчанию предоставляет встроенное, высокопроизводительное решение для отложенной загрузки . При использовании элемента <img> существует несколько вариантов отложенной загрузки, но все они имеют недостатки, которые затрудняют их использование. Разработчик может выбрать один из следующих подходов к отложенной загрузке:
- Укажите атрибут
loading: эта функция поддерживается всеми современными браузерами . - Используйте API Intersection Observer : создание собственного решения для отложенной загрузки требует усилий, продуманного проектирования и реализации. У разработчиков не всегда есть на это время.
- Импорт сторонней библиотеки для отложенной загрузки изображений: Для оценки и интеграции подходящей сторонней библиотеки для отложенной загрузки могут потребоваться дополнительные усилия.
В компоненте Image библиотеки Next.js загрузка по умолчанию установлена на "lazy" . Ленивая загрузка реализована с помощью Intersection Observer, который доступен в большинстве современных браузеров . Разработчикам не требуется предпринимать никаких дополнительных действий для его включения, но они могут отключить его при необходимости.
Предварительно загрузите важные изображения
Довольно часто элементами LCP являются изображения, и большие изображения могут задерживать загрузку LCP. Рекомендуется предварительно загружать важные изображения , чтобы браузер мог обнаружить их раньше. При использовании элемента <img> подсказку о предварительной загрузке можно включить в заголовок HTML следующим образом.
<link rel="preload" as="image" href="important.png">
Хорошо спроектированный компонент для работы с изображениями должен предоставлять возможность настраивать последовательность загрузки изображений независимо от используемого фреймворка. В случае компонента Image в Next.js разработчики могут указать изображение, которое является подходящим кандидатом для предварительной загрузки, используя атрибут priority компонента images.
<Image src="/hero.jpg" alt="hero" height="400" width="200" priority />
Добавление атрибута priority упрощает разметку и делает использование более удобным. Разработчики компонентов изображений также могут изучить возможности применения эвристических алгоритмов для автоматической предварительной загрузки изображений, отображаемых в верхней части страницы и соответствующих определенным критериям.
Поощряйте использование высокопроизводительных хостингов изображений.
Для автоматизации оптимизации изображений рекомендуется использовать CDN-сети , которые также поддерживают современные форматы изображений, такие как WebP и AVIF. Компонент Image в Next.js по умолчанию использует CDN-сеть с архитектурой загрузчика . В следующем примере показано, что загрузчик позволяет настраивать CDN в конфигурационном файле Next.js.
module.exports = {
images: {
loader: 'imgix',
path: 'https://ImgApp/imgix.net',
},
}
Благодаря такой конфигурации разработчики могут использовать относительные URL-адреса в источнике изображений, и фреймворк объединит относительный URL-адрес с путем CDN для генерации абсолютного URL-адреса. Поддерживаются популярные CDN для изображений, такие как Imgix , Cloudinary и Akamai . Архитектура поддерживает использование собственного облачного провайдера путем реализации пользовательской функции loader для приложения.
Поддержка образов, размещенных на собственном сервере.
В некоторых ситуациях веб-сайты не могут использовать CDN для изображений. В таких случаях компонент для работы с изображениями должен поддерживать самостоятельное размещение изображений. Компонент Next.js Image использует оптимизатор изображений в качестве встроенного сервера изображений, предоставляющего API, подобный CDN. Оптимизатор использует Sharp для преобразования изображений в рабочую среду, если он установлен на сервере. Эта библиотека — хороший выбор для тех, кто хочет создать собственный конвейер оптимизации изображений.
Поддержка прогрессивной загрузки
Прогрессивная загрузка — это метод, используемый для удержания внимания пользователей путем отображения изображения-заглушки, обычно значительно более низкого качества, пока загружается основное изображение. Он улучшает воспринимаемую производительность и повышает удобство использования. Его можно использовать в сочетании с отложенной загрузкой для изображений, отображаемых ниже или выше линии сгиба страницы.
Компонент Image в Next.js поддерживает прогрессивную загрузку изображения с помощью свойства placeholder . Это можно использовать в качестве LQIP (Low-quality image placeholder) для отображения изображения низкого качества или размытого изображения во время загрузки фактического изображения.
Влияние
Благодаря всем этим оптимизациям мы добились успеха с компонентом Next.js Image в продакшене и также работаем над аналогичными компонентами для обработки изображений с использованием других технологических стеков.
Когда компания Leboncoin перевела свой устаревший JavaScript-фронтенд на Next.js , она также обновила свой конвейер обработки изображений, используя компонент Image из Next.js. На странице, где загрузка изображений была перенесена с <img> на next/image, время загрузки LCP сократилось с 2,4 с до 1,7 с. Общий объем загруженных изображений уменьшился с 663 КБ до 326 КБ (при этом около 100 КБ было загружено с отложенной загрузкой).
Уроки, извлеченные из опыта
Любой, кто создает приложение на Next.js, может извлечь выгоду из использования компонента Next.js Image для оптимизации. Однако, если вы хотите создать аналогичные абстракции производительности для другого фреймворка или CMS, следующие несколько уроков, которые мы усвоили в процессе работы, могут оказаться полезными.
Предохранительные клапаны могут принести больше вреда, чем пользы.
В одной из ранних версий компонента Image в Next.js мы добавили атрибут unsized , который позволял разработчикам обходить требование указания размеров и использовать изображения с неуказанными параметрами. Мы считали, что это необходимо в случаях, когда невозможно заранее узнать высоту или ширину изображения. Однако мы заметили, что пользователи рекомендовали атрибут unsized в сообщениях на GitHub в качестве универсального решения проблем с требованием указания размеров, даже в тех случаях, когда проблему можно было решить способами, не ухудшающими CLS. Впоследствии мы отказались от использования атрибута unsized и удалили его.
Отделите полезные неудобства от бессмысленных неприятностей.
Требование к размеру изображения является примером «полезного ограничения». Оно ограничивает использование компонента, но взамен обеспечивает значительные преимущества в производительности. Пользователи охотно примут это ограничение, если будут четко понимать потенциальные преимущества в производительности. Поэтому стоит объяснить этот компромисс в документации и других опубликованных материалах о компоненте.
Однако можно найти обходные пути для решения подобных проблем без ущерба для производительности. Например, во время разработки компонента Image в Next.js мы получали жалобы на неудобство поиска размеров локально хранящихся изображений. Мы добавили импорт статических изображений , который упрощает этот процесс, автоматически получая размеры локальных изображений во время сборки с помощью плагина Babel.
Найдите баланс между удобными функциями и оптимизацией производительности.
Если ваш компонент изображения не делает ничего, кроме как создает «полезные неудобства» для пользователей, разработчики, как правило, не захотят его использовать. Мы обнаружили, что, хотя такие функции, как изменение размера изображения и автоматическая генерация значений srcset , оказались наиболее важными, удобные для разработчиков функции, такие как автоматическая отложенная загрузка и встроенные размытые заполнители, также повысили интерес к компоненту изображения Next.js.
Составьте план внедрения функций, чтобы стимулировать их использование.
Создать решение, идеально подходящее для всех ситуаций, очень сложно. Может возникнуть соблазн разработать что-то, что хорошо работает для 75% людей, а затем сказать остальным 25%, что «в этих случаях этот компонент вам не подходит».
На практике эта стратегия оказывается в противоречии с вашими целями как разработчика компонентов. Вы хотите, чтобы разработчики использовали ваш компонент, чтобы воспользоваться его преимуществами в плане производительности. Это сложно сделать, если есть группа пользователей, которые не могут перейти на ваш компонент и чувствуют себя обделенными вниманием. Они, скорее всего, выразят разочарование, что приведет к негативному восприятию и повлияет на внедрение.
Желательно иметь план развития компонента, охватывающий все разумные сценарии использования в долгосрочной перспективе. Также полезно четко указывать в документации, что не поддерживается и почему, чтобы сформировать ожидания относительно проблем, которые компонент призван решать.
Заключение
Использование и оптимизация изображений — сложный процесс. Разработчикам приходится искать баланс между производительностью и качеством изображений, обеспечивая при этом удобство использования для пользователей. Это делает оптимизацию изображений дорогостоящим и эффективным мероприятием.
Вместо того чтобы заставлять каждое приложение изобретать велосипед заново, мы разработали шаблон передовых практик, который разработчики, фреймворки и другие технологические стеки могут использовать в качестве参考 для своих собственных реализаций. Этот опыт, безусловно, окажется ценным при поддержке других фреймворков и их компонентов обработки изображений.
Компонент Image в Next.js успешно улучшил производительность приложений Next.js, тем самым повысив удобство использования. Мы считаем, что это отличная модель, которая хорошо бы работала в более широкой экосистеме, и будем рады услышать от разработчиков, желающих внедрить эту модель в свои проекты.







