Как стать автором
Обновить

Максимально оптимизированная веб-загрузка изображений в 2021 году

Время на прочтение 6 мин
Количество просмотров 28K
Всего голосов 29: ↑29 и ↓0 +29
Комментарии 71

Комментарии 71

НЛО прилетело и опубликовало эту надпись здесь

Я недавно попробовал avif. Экспортировал из последнего GIMP. При уровнях качества около 80-90% изображение содержало заметные артифакты по сравнению с jpeg, а галочка lossless выдала картинку, чей объем на диске чудовищно превосходил исходник в jpeg.

Может, стоило попробовать конвертировать из PNG (оригинал) в JPEG и AVIF, и уже такие результаты сравнивать? Ведь JPEG -> AVIF предсказуемо наложит только новые артефакты на уже имеющиеся в JPEG. А размер lossless сравнивать тоже с PNG оригиналом, поскольку качества пиксель-в-пиксель в JPEG нет именно из-за размеров.

lossless оригиналов у меня не было.
Я пробовал на других PNG. Результаты были разные, кое-какие ужались в объеме очень здорово, а кое-какие… совсем не очень:(
Но в целом если комбинировать webp и avif по мере надобности, выигрыш хороший.

Помимо AVIF, там ещё JPEG XL и WebPv2 грядут. Похоже, что намечается небольшая войнушка форматов. Но тут забавно то, что в разработку всех этих форматов вовлечён Google.
Не заслужено упустили webP. А по тихому произошло крайне важное событие — уже 92% всех браузеров пользователей поддерживают его. Почти пропал смысл использовать на сайтах png/jpeg
У WebP есть проблема: он не всегда лучше JPEG или PNG. Google с ним слишком поспешил. Лучше дождаться JPEG XL, который уже гораздо более продуман. Даже lossless режим для обычного JPEG предусмотрен (то есть существующие JPEG можно дожать без потери качества). Формат JPEG XL недавно был заморожен, идёт стандартизация, разработчики Chromium уже занялись реализацией его поддержки.
Проблема не в браузерах, как может показаться, а в CMS. Вот когда они начнут из коробки понимать WEBP, AVIF, HEIF… Сейчас просто нет физической возможности пользователю загрузить в тот же Вордпресс картинку в чем-то ином нежели png,gif,jpg,bmp

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


Ну или же, при боязне плагинов — есть хук из пары строк для добавления поддержки любого нового формата.

Какой плагин посоветуете? Мне не надо, чтобы он конвертировал фото из джипега в вебп. Мне надо просто возможность загружать вебп в вордпресс; типа мышкой перетащить файл вебп в медиафайлы.
Если совсем простой — WP Add Mime Types. А если хочется прям самому, то есть фильтры mime_types и upload_mimes, например так:

function mihdan_edit_upload_types( $existing_mimes = array() ) {
    // allow .woff
    $existing_mimes['woff'] = 'font/woff';
 
    // disallow .jpg files
    unset( $existing_mimes['jpg'] );
 
    return $existing_mimes;
}
add_filter( 'upload_mimes', 'mihdan_edit_upload_types' );
С 5.1 версии WP для SVG файлов надо еще на хуке wp_check_filetype_and_ext изменить проверку заголовка, так как иногда в svg может отсутствовать заголовок xml, ну или ручками добавлять везде тот заголовок

Тут подробней
Плагин Allow WebP image, так и называется. Пробовал в последней версии Wordpress.
Он с коробки специально фильтрует то что на 100% не поддерживается. Сделано это больше с целью обезопасить блогера, чтоб он не закидывал на сервер что не попадя.

Вот когда они начнут из коробки понимать WEBP, AVIF, HEIF…

Боюсь никогда, для генерирования этого (а WP из коробки как минимум нарезает миниатюры) всего надо в php добавлять библиотеки, и далеко не каждый хостер этим вообще будет заниматься.

Динамически можно определять список поддерживаемых

НЛО прилетело и опубликовало эту надпись здесь

Никаким образом не затрудняет. Через html-элемент picture можно указывать ссылки на разные типы картинок. Браузер определяет, есть ли поддержка webp и качает картинку нужного типа. Вот пример, где я так сделал.

НЛО прилетело и опубликовало эту надпись здесь

Ок, а как именно "очень сильно затрудняет SEO по нынешним временам"?

НЛО прилетело и опубликовало эту надпись здесь

Тогда понял, спасибо

Самое главное правило забыли: использовать картинку только нужного размера. А то как вставят 4к изображение в элемент 150px шириной — так тут никакая оптимизация скорости не поможет, тут надо, простите, мозги сначала починить.
Для этого и есть упомянутый в статье srcset.

Спасибо за статью. До этого использовал либу, чтобы инициализировать lazy загрузку. А оказывается все уже и без нее работает))

"Cumulative Layout Shift (CLS)" — спасибо, теперь буду знать, что у этого супер-бесячего эффекта есть название.

НЛО прилетело и опубликовало эту надпись здесь

А как надо изменить? Чтобы это было управляемо разработчиками? Чтобы это приносило больше пользы чем проблем?

НЛО прилетело и опубликовало эту надпись здесь

Lazy loading для своей работы требует height и width. Если их не будет, то будет происходить пересчет layout'а слишком часто, а это вызывает проблемы с позиционированием в других местах. А брать эти значения из самого изображения нельзя без загрузки самого изображения, то есть руками все же надо указывать метаинформацию.
Но это все ещё не решает проблему с объемом изображения, который сейчас решается через srcset.


А что вам надо подстраивать в lazy loading стандартном?


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

НЛО прилетело и опубликовало эту надпись здесь
И при выходе новой версии браузера бегать по всем сайтам и смотреть что-где поломалось, так как браузер изменил свое поведение по умолчанию?
НЛО прилетело и опубликовало эту надпись здесь
Так он там и ломает, где изменилось поведение и как правило это что-то эксперементальное, но не то что уже десятилетиями и прописано в стандартах
НЛО прилетело и опубликовало эту надпись здесь
Размытая заглушка

Зачем в svg прятать png? Почему просто png туда не замостить?
НЛО прилетело и опубликовало эту надпись здесь

ехал data через data… видит data в реке data

Потому что png микроскопический (9х6) содержит просто набор разноцветных пикселей. А svg используется ради наложения эффекта размытия.

У меня другой вопрос. Зачем их инлайново вставлять?

И еще возможно лучше вместо png использовать собственно svg для создания цветных пятен, которые необходимо размыть. Или так будет хуже размытие и общий вид заглушки?
НЛО прилетело и опубликовало эту надпись здесь
А заглушка предполагается, что будет генерироваться сервером для каждого изображения индивидуально на основе оригинального изображения? Я думал, что речь о какой-то одной для всех изображений заглушке просто для обозначения того, что тут будет загружена картинка и для того, чтоб это выглядело симпатично. Потому счёл, что сделать один файл, который при первой загрузке закешируется будет лучше.
НЛО прилетело и опубликовало эту надпись здесь

А зачем не-инлайново их вставлять?

Интересно сколько десятилетий нужно web-у чтобы сделать <img/> или <picture/> действительно адаптивным. А не то что есть сейчас. Сейчас все эти srcset-ы скачут от размера окна, что само по себе весьма бредовая идея, т.к. она предполагает что вы, размещая картинку, всегда в деталях знаете её точный размер и размер окна. Я даже не знаю где это за пределами блогов\лендингов нужно.


В то время как очевидно, что вот такое вот решение:


<picture aspectratio="1/2">
  <sоurce src="..." width="1000"/>
  <sоurce src="..." width="700"/>
  <sоurce src="..." width="200"/>
</picture>

Решило бы 99% всех задач гораздо лучше. У браузера итак есть всё что нужно:


  • он знает pixel density
  • он знает размер <img/>
  • он знает пропускную способность сети

А мы в свою очередь можем предоставить ему недостающее:


  • список файлов и их размеры
  • aspect ratio (одинаковый для всех файлов)

А все эти полумеры с srcset, sizes это прямо какой-то детский сад с очень ограниченным применением (мне пока ни разу не пригодилось, т.к. не было высеченных в скале layout-ов).

Было бы гораздо проще если бы браузер использовал width и height, как пропорции и сам закладывал высоту блока пропорционально, и учитывая max-width: 100% для img

Это уже есть, недавно завезли css-свойство aspect-ratio. Моё сообщение совсем о другом.

НЛО прилетело и опубликовало эту надпись здесь

А что не так с CSS-костылями? Ну кроме малой поддержки браузерами на данный момент? Если речь про отсутствие attribute-а, то <img style=" никто не отменял.

НЛО прилетело и опубликовало эту надпись здесь
В то время как очевидно, что вот такое вот решение:
<picture aspectratio="1/2">
  <sоurce src="..." width="1000"/>
  <sоurce src="..." width="700"/>
  <sоurce src="..." width="200"/>
</picture>

Решило бы 99% всех задач гораздо лучше.


Так оно вроде так и работает
<picture>
 <source srcset="mdn-logo-wide.png" media="min-width: 600px">
 <img src="mdn-logo-narrow.png" alt="MDN">
</picture>

Прописал условия и вуаля

У браузера итак есть всё что нужно:
  • он знает pixel density
  • он знает размер
  • он знает пропускную способность сети

Он ведь узнает это все когда загрузит картинку, а в вашем примере их три и получается надо грузить все три что узнать то что ему и не надо. Или я не правильно вас понял?

А все эти полумеры с srcset, sizes это прямо какой-то детский сад с очень ограниченным применением (мне пока ни разу не пригодилось, т.к. не было высеченных в скале layout-ов).

А зачем вам высеченный в скале шаблон, он же адаптивный должен быть и размера экрана как раз вполне хватает. Как правило выделяет два размера для ПК и моб, иногда еще один для планшетов(но чаще он совпадает с ПК). Я смотрю максимальную ширину для каждого шаблона и накидываю 20% чтоб не терялось качество, и вот под них пишем правила.
Все, браузер грузит только одну картинку. Гугл на 20% превышение ширины не ругается в своих оценках, скорость загрузки для мобильных заметно возрастает, контент не страдает.

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

А вот aspect-ratio и правда иногда не хватает, помню когда-то для магазина пришлось допиливать костыль на js

Если я правильно понял faiwer (и в этом с ним трудно не согласиться), то неудобство состоит в том, что значение атрибута media применяется к экрану и может быть никак не связано с размером самой картинки. Например, у картинки через стили может быть указано max-width:100px (или ширина ограничена неявно через стили родителя), но раз в media-атрибуте указано, что для экранов с шириной больше 600px нужно грузить большую картинку — браузер будет грузить большую картинку.

Так оно вроде так и работает

Нет. Оно так НЕ работает.


<sourсe srcset="mdn-logo-wide.png" media="min-width: 600px">

Вот тут min-width это не размер картинки. Это размер окна. По сути мусор.


и размера экрана как раз вполне хватает.

Ни разу не хватает. Я даже больше скажу это вообще не тот фактор который играет роль. Мне глубоков плевать какого размера окно. Единственное что важно — какого размера сам контейнер картинки. И он может быть вообще не связан с размером окна. Либо связан столь причудливым образом, что любая попытка эту взаимосвязь выставить будет безумной.


У меня встречались случаи, когда надо не просто разные миниатюры одного изображения показывать, а абсолютно разные картинки под разные устройства и вот с этим Picture очень даже справляется.

А вот это очень редкий кейс. Но да, для него уже что-то есть.


Я потратил много времени на написание нормального Picture компонента, который, используя ResizeObserver, работает как надо (за исключением того что ResizeObserver очень ненадёжная штука). И у меня бомбит от того, что из каждого утюга пишут про то какой <picture/> хороший, при том какой он плохой. Прямо стыдно за коммитет, что они такую херню утвердили.

Я правильно понял: вы хотите чтобы браузер грузил url картинки в зависимости от ширины контейнера в котором находится эта картинка?

А вот если ширина контейнера не задана тогда что грузить, что-то по дефолту?

Идем дальше. Рядом с картинкой идет текст без переносов и он не помещается в свой блок, браузер начинает адаптировать его и уменьшает контейнер с вашей картинкой, что тогда, грузить еще одну миниатюру?

Ни разу не хватает. Я даже больше скажу это вообще не тот фактор который играет роль. Мне глубоков плевать какого размера окно.

Если у вас ширина картинок задана жестко то да ширина экрана вам и не нужна, но если вы знаете этот размер то можете туда и нужную картинку подставить. Ну разве что у вас этот размер меняется от ширины экрана но тут опять же, вы будете это делать через медиа.

Если у вас ширина контейнера(в котором будет картинка) задана относительно, то она точно зависит от ширины окна и тут снова медиа все решает.
Да бывают случаи когда заказчик хочет чтоб у него под все возможные ширины экрана были личные стили, но это уже зайоб заказчика и вот тогда приходится костылями на js решать, но чаще всего медиа достаточно.

Я потратил много времени на написание нормального Picture компонента, который, используя ResizeObserver, работает как надо (за исключением того что ResizeObserver очень ненадёжная штука). И у меня бомбит от того, что из каждого утюга пишут про то какой <picture/> хороший, при том какой он плохой. Прямо стыдно за коммитет, что они такую херню утвердили.

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

Браузер не может ждать пока отрендерится весь контент, чтоб точно знать ширину контейнера и определять, что ему грузить. Если бы так было то мы бы вернулись в 2000-е к IE который сначала текст отрисовывал, а потом по очереди грузил нужные картинки.

Собственно я это к чему все написал: во время загрузки ширина окна не меняется, она заранее известна и можно сходу понять какую картинку загрузить на основании media-правил, не ожидая когда оно там все отрисует.
Я правильно понял: вы хотите чтобы браузер грузил url картинки в зависимости от ширины контейнера в котором находится эта картинка?

Под контейнером я подразумеваю сам тег <img/>. Не его родительский условный <div/>, а сам. Т.е. тот самый прямоугольник в котором браузер будет рисовать картинку.


А вот если ширина контейнера не задана тогда что грузить, что-то по дефолту?

Нет. Браузер знает фактический размер контейнера в котором рисует картинку (<img/>, <picture/>). Как он задан — второстепенный вопрос. Главное что итоговый размер известен браузеру.


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


и оно само там как-то как догадывалось заранее кокой ширины будет контейнер.

Оно знает размер контейнера. Это браузер. Это render фаза. Весь CSS + HTML layout уже просчитан. Ему не нужно догадываться. Это вводные данные.


и можно сходу понять какую картинку загрузить на основании media-правил,

Только в самых тривиальных случаях. Размер картинки может зависеть от десятка параметров, среди которых размер окна может вообще отсутствовать.


То есть вы сами говорите, что это не тривиальная задача, но хотите чтоб это встроили в ядро

Да. Я хочу чтобы это встроили в ядро. Вместо этого убожества, что есть сейчас. Заодно я хочу content queries. Надеюсь я доживу до того дня, когда их наконец внедрят.

НЛО прилетело и опубликовало эту надпись здесь
Вам намекают на то, что сейчас браузер может начать грузить изображения сразу после разбора HTML

Интересный момент. Не думал о нём, т.к. пишу только SPA последние много-много лет. Т.е. мне совсем не актуально. Но ок, представим себе ситуацию:


  • у нас есть множество файлов изображений с одинаковым aspect-ratio
  • мы хотим чтобы браузер грузил нужный файл ещё ДО того как он смог организовать render
  • для этого нам нужно гарантированно задать изображению точные размеры

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

Нет. Браузер знает фактический размер контейнера в котором рисует картинку (, <picture/>). Как он задан — второстепенный вопрос. Главное что итоговый размер известен браузеру.

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

Вот к примеру визуализация данной страницы, когда уходит запрос и когда закончен рендеринг. Запросы на картинки ушли на 2,2 сек, а применение стилей закончилось только 2,8 сек, а бывает что подключение стилей объявляют в футере и они отрендерятся (будет известен точный размер блока картинки) еще позже.

Вот можете сами убедиться о чем знает браузер и когда пример на codepen.io (Только открывайте с чисткой кэша)

А дальше откройте консоль к примеру этой странице на закладке network и посмотрите когда отработало событие DOMContentLoaded и load. В браузере большинство мелких картинок уже загружено еще до события DOMContentLoaded.

О, теперь понял вас. Вы про ситуацию когда стили ещё не загружены. Тысячу лет с таким не сталкивался, т.к. последние годы работал только с SPA. Для SPA это чаще не актуально (хотя зависит от имплементации). Т.к. HTML уже инжектится на страницу со всеми необходимыми стилями. Т.е. размеры известны сразу после appendChild. И DOMContentLoaded давно позади.


Касательно immediate загрузки без стилей. Ну тут нужно думать. Варианты есть, но уже не так всё однозначно. Например отложить загрузку пока изображение равно 0х0. Меня бы вполне устроило. Это точно лучше чем грузить что попало.

Либо я чего-то не понимаю, но как быть вот с этим при адаптивном дизайне


<!-- Providing width and height is more important than ever. -->
<img height="853" width="1280" … />

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

И еще вопрос про srcset и sizes, какой в этом смысл. Если уже давно число пикселей расчитывается сложнее


Число физических (реальных) пикселей равно числу CSS-пикселей, умноженному на соотношение пикселей (pixel ratio).


Век экранов, у которых число реальных и CSS-пикселей совпадает, давно кончился. Помните, когда-то Apple революционно вышла на рынок с ретина-дисплеями? Это когда pixel ratio — 2. На Samsung Galaxy S6 2015 года соотношение физических пикселей к CSS-пикселям 4! Большинство китайских смартфонов сегодня — это pixel ratio 2-3.


А значит, если у нас на мобилке картинка занимает 360px, нет смысла загружать иллюстрацию шириной меньше, чем 360×2=720px! А обладатель корейского флагмана был бы не против и все 1440px загрузить.


Так что в этой штуке очень мало смысла, возможно кто-то меня перубедит

НЛО прилетело и опубликовало эту надпись здесь
А обладатель корейского флагмана был бы не против и все 1440px загрузить.

Особенно когда кроме EDGE ничего не ловится

И w — дескриптор ширины. Это очень удобно, если запомнить, что w — это реальные пиксели, а px — CSS-пиксели, и тогда работа srcset/sizes станет еще понятнее.

Обычно вебмастера этого и не понимают

loading=«lazy» пока не починят в хроме применять не стоило бы.
НЛО прилетело и опубликовало эту надпись здесь

Браузеры вообще — зло

А что с ним не так, простите

В текущей версии не проверял, но в предыдущих, на медленных соединениях, хром грузил 7 экранов с изображениями, вместо одного.
Столько негодования. И то не так и это.

Я тут один помню времена, когда не было flex и grid и колонки делались через float:left и это было очень прогрессивно, так как многие еще и таблицы использовали :).
Или для видео надо было мудрить флеш-плейер с js. А сейчас оно из коробки может показывать.
А уж про адаптацию под три-четыре браузера вообще вспоминать не хочется (хотя иногда приходится под сафари что-то доделывать)

За статью спасибо про некоторые подходы не знал.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий