3/27/2022

Очевидности оптимизации картинок: Часть 1

Очевидности оптимизации картинок: Часть 1

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

Поехали 🚀

Есть два способа оптимизции: статическая (static) и динамическая (on-the-fly).

О том, почему оптимизации важны, что и как оптимизировать, я расскажу чуть позже, в другом посте. И да, я буду немножко капитанить :)

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

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

Почему одна картинка может быть важнее другой? Например, потому, что эта картинка — самый большой элемент на странице, и от нее зависит наш LCP score.

Еще один вариант – когда картинка каким-то образом связана с важным для нас действием пользователя: например, баннер, где мы предлагаем купить подписку на что-нибудь :)

Если вы накачаете свои оптимизационные мускулы, то, скорее всего, ручная оптимизация будет у вас получаться лучше автоматической. А до тех пор, пока это не так, есть next/image (ой, спойлеры :)

Отлично! Наша картинка пожата, в процессе сжимания никто не пострадал, и мы готовы запихнуть все на production. Что произойдет дальше?

Если вы заботитесь о производительности, то, скорее всего, у вас настроен CDN. Когда пользователи запрашивают ваши изображения, они кешируются на CDN.

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

Окей. А что делать, если картинки на продакшн грузите не вы, а ваши пользователи (ведь каждому же нужен свой собственный альбом с котиками)?

В этом случае у вас больше проблем и меньше контроля. И тут приходится использовать второй способ – динамические оптимизации.

Как это работает? Тут проще будет показать на примере. И в качестве примера у нас, конечно же, imgproxy :) Для других инструментов все будет примерно похоже.

  1. Вы генерируете специальный url, в который зашиваете параметры для оптимизации. Например, так:
  1. Вместо url оригинальной картинки вы используете только что сгенерированный. При этом хорошей практикой будет закодировать полученный url в base-64.
  2. Теперь, когда ваши пользователи попросят картинку, вместо того, чтобы грузить оригинал, они пойдут к imgproxy.
  3. imgrpoxy на лету распарсит url и на основе параметров решит, что нужно делать: пережать, отресайзить, шлепнуть watermark, и все, что вы попросите (в пределах разумного) :) Преобразовав картинку, imgproxy радостно отдаст ее пользователю.
  4. Но, так как мы заботимся о производительности, разумеется, у нас есть CDN. И, разумеется, картинка (уже та, что сгенерил нам imgproxy) закешируется.

Дальше все как со статикой. Большинство наших картинок раздаются CDN, imgproxy надо будет только немножко поработать, пока кеши не прогрелись.

Некоторые зададутся вопросом: а стоит ли динамическая оптимизация подобных усилий, может, проще загрузить оригинал? Ведь у нас появляется куча дополнительных затрат: imgproxy надо скачать картинку и запроцессить ее. Может, это будет намного дольше чем загрузить изображение as is?

Но у нас же все спрятано за CDN, помните? Поэтому процессинг нам понадобится только до тех пор, пока картинка не попала в кеш. Более того, imgproxy (как и многие другие инструменты) работает очень-очень быстро.

Давайте на примере. Я загрузила фоточку на 1MБ размером 2000 x 2000. На моей чудесной страничке все контейнеры для картинок размером 200 x 200. Поэтому нам нужна картинка размером 400 x 400 (привет, ретина). imgproxy сжимает нам картинку до 30КБ (циферки немножко округленные, но взяты из реального эксперимента). Хотим ли мы сэкономить 970КБ.... ну низнааааю 😉

И еще один важный момент: imgproxy (и другие подобные инструменты) могут не только оптимизировать контент, но и защитить нас от некоторых атак, связанных с изображениями. Несмотря на такую полезную защиту, я настоятельно рекомендую никогда не позволять вашим пользователям загружать SVG :)

Например потому что вот такой кружочек на самом деле не кружочек

<svg>
    <circle cx="50" cy="50" r="40" />
    <script>
        document.body.innerHTML = `👻 Booo!`;
    </script>
</svg>

Браузер старательно выполнит вложенный скрипт

Чтобы избежать этой проблемы и прочих бяк, настройте CSP.

Но, если у нас уже есть инструмент для динамической оптимизации, может, нам и статическая не нужна? 🙂

Статическая оптимизация всегда эффективней динамической (ведь у вас полный контроль, помните?) Но, учитывая наличие кешей, может оказаться, что выигрыш не стоит дополнительных усилий.

Хотя я предпочитаю использовать оба подхода. Статику сжимать статически, а динамику – динамически :)