Создание эффективного компонента изображения

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

Лина Сохони
Leena Sohoni
Кара Эриксон
Kara Erickson
Алекс Кастл
Alex Castle

Изображения являются распространенным источником проблем с производительностью веб-приложений и ключевым направлением оптимизации. Неоптимизированные изображения способствуют раздуванию страницы и в настоящее время составляют более 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 представляет функцию «Приоритет выборки» в качестве пробной версии Origin . Его можно использовать для повышения приоритета определенных изображений на странице. В целом, разработчикам было бы проще, если бы такие улучшения оценивались и реализовывались на уровне компонентов.

Имиджевый компонент как решение

Доступные возможности оптимизации изображений и трудности их реализации индивидуально для каждого приложения привели нас к идее компонента изображения. Компонент изображения может инкапсулировать и применять лучшие практики. Заменив элемент <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. При использовании компонента изображения 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" />
}

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

Облегчение реагирования

Чтобы изображения реагировали на разные устройства, разработчики должны установить атрибуты srcset и sizes в элементе <img> . Мы хотели сократить эти усилия с помощью компонента «Изображение». Мы разработали компонент изображения Next.js так, чтобы значения атрибутов устанавливались только один раз для каждого приложения. Мы применяем их ко всем экземплярам компонента Image в зависимости от режима макета. Мы придумали решение, состоящее из трёх частей:

  1. Свойство deviceSizes : это свойство можно использовать для однократной настройки точек останова на основе устройств, общих для базы пользователей приложения. Значения по умолчанию для точек останова включены в файл конфигурации.
  2. Свойство imageSizes : это также настраиваемое свойство, используемое для получения размеров изображения, соответствующих контрольным точкам размера устройства.
  3. Атрибут layout в каждом изображении: используется для указания того, как использовать свойства deviceSizes и imageSizes для каждого изображения. Поддерживаемые значения для режима макета: fixed , fill , intrinsic и responsive

Когда изображение запрашивается с режимами макета «отзывчивый» или «заливка» , Next.js идентифицирует изображение, которое будет обслуживаться, на основе размера устройства, запрашивающего страницу, и соответствующим образом устанавливает srcset и sizes изображения.

Следующее сравнение показывает, как режим макета можно использовать для управления размером изображения на разных экранах. Мы использовали демонстрационное изображение , опубликованное в документации Next.js, просмотренное на телефоне и стандартном ноутбуке.

Экран ноутбука Экран телефона
Макет = Внутренний: масштабируется до ширины контейнера на меньших окнах просмотра. Не масштабируется за пределы внутреннего размера изображения в большом окне просмотра. Ширина контейнера равна 100 %.
Изображение гор показано как естьИзображение гор уменьшено
Макет = Исправлено: изображение не реагирует. Ширина и высота фиксированы аналогично ` `элемент независимо от устройства, на котором он отображается.
Изображение гор показано как естьИзображение гор в том виде, в каком оно есть, не умещается на экране.
Макет = Адаптивный: масштабирование в зависимости от ширины контейнера в разных окнах просмотра с сохранением соотношения сторон.
Изображение гор увеличено до размера экранаИзображение гор уменьшено до размера экрана
Макет = Заливка: ширина и высота растягиваются для заполнения родительского контейнера. (Родитель `
` в этом примере ширина установлена ​​на 300*500)
Изображение гор рендерится под размер 300*500.Изображение гор рендерится под размер 300*500.
Изображения, визуализированные для разных макетов

Обеспечьте встроенную отложенную загрузку

Компонент Image по умолчанию предоставляет встроенное высокопроизводительное решение для отложенной загрузки . При использовании элемента <img> есть несколько встроенных вариантов отложенной загрузки, но все они имеют недостатки, которые затрудняют их использование. Разработчик может использовать один из следующих подходов отложенной загрузки:

  • Укажите атрибут loading : это легко реализовать, но в настоящее время не поддерживается в некоторых браузерах.
  • Используйте API Intersection Observer . Создание собственного решения для отложенной загрузки требует усилий, а также продуманного проектирования и реализации. У разработчиков не всегда может быть на это время.
  • Импортируйте стороннюю библиотеку для отложенной загрузки изображений. Могут потребоваться дополнительные усилия для оценки и интеграции подходящей сторонней библиотеки для отложенной загрузки.

В компоненте изображения Next.js по умолчанию загрузка установлена ​​на "lazy" . Отложенная загрузка реализована с помощью Intersection Observer, который доступен в большинстве современных браузеров . Разработчикам не требуется делать ничего дополнительного, чтобы включить его, но они могут отключить его при необходимости.

Предварительная загрузка важных изображений

Довольно часто элементами LCP являются изображения, а большие изображения могут задерживать LCP. Рекомендуется предварительно загрузить важные изображения , чтобы браузер мог быстрее обнаружить их. При использовании элемента <img> подсказка о предварительной загрузке может быть включена в заголовок HTML следующим образом.

<link rel="preload" as="image" href="important.png">

Хорошо спроектированный компонент изображения должен предлагать возможность настройки последовательности загрузки изображений независимо от используемой платформы. В случае с компонентом изображения Next.js разработчики могут указать изображение, которое является подходящим кандидатом для предварительной загрузки, используя атрибут priority компонента изображений.

<Image src="/hero.jpg" alt="hero" height="400" width="200" priority />

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

Поощряйте высокопроизводительный хостинг изображений

CDN изображений рекомендуется для автоматизации оптимизации изображений, они также поддерживают современные форматы изображений, такие как WebP и AVIF. Компонент Next.js Image по умолчанию использует 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 использует оптимизатор изображений в качестве встроенного сервера изображений, который предоставляет CDN-подобный API. Оптимизатор использует Sharp для преобразования производственных изображений, если он установлен на сервере. Эта библиотека — хороший выбор для тех, кто хочет создать собственный конвейер оптимизации изображений.

Поддержка прогрессивной загрузки

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

Компонент Next.js Image поддерживает прогрессивную загрузку изображения через свойство заполнителя . Его можно использовать как LQIP (заполнитель изображения низкого качества) для отображения изображения низкого качества или размытия во время загрузки фактического изображения.

Влияние

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

Когда Leboncoin перенесла свой устаревший интерфейс JavaScript на Next.js , они также обновили свой конвейер изображений, чтобы использовать компонент изображения Next.js. На странице, которая перешла с <img> на next/image, время LCP снизилось с 2,4 до 1,7 с. Общий объем байтов изображений, загруженных для страницы, увеличился с 663 КБ до 326 КБ (примерно 100 КБ байтов изображений, загруженных с отложенной загрузкой).

Уроки выучены

Любой, кто создает приложение Next.js, может извлечь выгоду из использования компонента Next.js Image для оптимизации. Однако, если вы хотите создать аналогичные абстракции производительности для другой платформы или CMS, следующие уроки, которые мы извлекли на этом пути, могут оказаться полезными.

Предохранительные клапаны могут принести больше вреда, чем пользы

В ранней версии компонента Next.js Image мы предоставили атрибут unsized , который позволял разработчикам обойти требования к размеру и использовать изображения с неуказанными размерами. Мы думали, что это будет необходимо в тех случаях, когда невозможно заранее узнать высоту или ширину изображения. Однако мы заметили, что пользователи рекомендуют атрибут unsized в проблемах GitHub как универсальное решение проблем с требованиями к размеру, даже в тех случаях, когда они могут решить проблему способами, не ухудшающими CLS. Впоследствии мы объявили устаревшим и удалили атрибут unsized .

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

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

Однако вы можете найти обходные пути для устранения таких проблем, не жертвуя при этом производительностью. Например, во время разработки компонента Next.js Image мы получали жалобы на то, что раздражает поиск размеров локально хранящихся изображений. Мы добавили импорт статических изображений , который упрощает этот процесс за счет автоматического получения размеров локальных изображений во время сборки с помощью плагина Babel.

Найдите баланс между удобными функциями и оптимизацией производительности.

Если ваш компонент изображения не делает ничего, кроме навязывания «полезного трения» своим пользователям, разработчики, как правило, не захотят его использовать. Мы обнаружили, что, хотя функции производительности, такие как изменение размера изображения и автоматическое создание значений srcset , были наиболее важными. Удобные функции для разработчиков, такие как автоматическая отложенная загрузка и встроенные размытые заполнители, также вызвали интерес к компоненту Next.js Image.

Создайте дорожную карту для функций, способствующих внедрению

Создать решение, которое идеально работало бы во всех ситуациях, очень сложно. Может возникнуть соблазн разработать что-то, что хорошо работает для 75% людей, а затем сказать остальным 25%, что «в этих случаях этот компонент не для вас».

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

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

Заключение

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

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

Компонент Next.js Image успешно улучшил производительность приложений Next.js, тем самым улучшив удобство работы с пользователем. Мы считаем, что это отличная модель, которая будет хорошо работать в более широкой экосистеме, и нам бы хотелось услышать мнение разработчиков, которые хотели бы использовать эту модель в своих проектах.