Ускорение загрузки сайта: туториал для джунов

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

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

В начале — пару слов о себе. Меня зовут Полина и я работаю «Rocket Business» уже несколько лет. Свой путь от джуна до тимлида прошла именно здесь. Поэтому своим мини-пособием решила делиться в корпоративном блоге: оставляю в помощь потомкам и аудитории Хабра. 

Тут — кликабельное оглавление для удобной навигации.

  1. Инструменты проверки.

  2. Кэширование.

  3. Рендеринг.
    3.1. Блокировка рендеринга.

  4. Оптимизация картинок.
    4.1. Сжатие.
    4.2. Конвертация.

  5. Еще немного об оптимизации картинок: повышение LCP.

  6. «Удалите неиспользуемый код JavaScript».

  7. Короткие выводы.

Инструменты проверки

Для начала, наши инструменты проверки.

  1. Google Pagespeed. Все seo-шники и клиенты сверяются по нему. Но довольно часто он врет и крутит баллы как в плюс, так и в минус. Дает список рекомендаций для повышения оценки. Мы стремимся к зеленой зоне – 90 и выше. 

  2. Google Lighthouse. То же самое, что и Pagespeed, но во вкладке Chrome. Врет меньше, часто показывает больше рекомендаций, чем Pagespeed.

  3. Остальные инструменты разработки Google Chrome. В частности вкладка Network – тут обращаем внимание на порядок, объем и waterfall загруженных ресурсов. Также используем вкладку Performance, тут можно детально изучить процесс загрузки страницы, время рендеринга, подключение к ресурсам и тайминги основных метрик Core Web Vitals.

Кэширование

Первым делом стоит изучить вкладку Network. Смотрим, сколько времени отдается сам html-документ и сколько он весит. Возможно, есть проблемы с кэшированием.

Например, тут все очень плохо:

Сам Pagespeed тоже это не пропустит:

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


Потребовалось пересмотреть настройки кэширования и стало лучше:

Если с кэшированием все хорошо, нужно искать проблему на хостинге. Она может быть в медленных дисках.

Кстати, 301-й ответ сервера тоже замедляет страницу на несколько сотен миллисекунд. По возможности нужно держать актуальные ссылки на страницах.

С ответом сервера разобрались, переходим к рендерингу.

Рендеринг

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

  • Важно не раздувать DOM: чем больше в нем элементов и уровень вложенности, тем дольше их будет обрабатывать браузер.

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

  • Лучше, чтобы было как можно меньше js-кода, изменяющего страницу: может упасть показатель CLS. Используем JS для интерактива.

Изучить процесс рендеринга можно на вкладке Performance в Chrome.

Блокировка рендеринга

В Lighthouse и Pagespeed есть рекомендация «Устраните ресурсы, блокирующие отображение».

Несколько рекомендаций:

  1. Есть скрипты в шапке? По возможности переноси все в футер (jquery и подобное, конечно, оставить, иначе js на части страниц упадет). Добавляем async или defer.

  2. В шапке подключаются гугл-шрифты? Подключи их локально, так как на коннект и скачивание их с сервера гугла уходит время. Вот хороший сервис для локализации гугл-шрифтов.

  3. Всем шрифтам (не только гугл) обязательно добавляем font-display: swap в подключении, чтобы текст отображался до загрузки шрифтов (FOIT).

  4. Есть подключения с cdn в шапке? Также, как и с гугл-шрифтами, тянем локально.

  5. Критичный CSS — спорная штука, лучше просто не раздувать стили по сайту.

  6. Если все-таки нужно в начале страницы подключиться к внешнему ресурсу, используй dns-prefetch.

  7. Если есть ресурсы, которые 100% будут на странице и влияют на отображение (например, стили), то можно предзагрузить их, используя prefetch.

В дополнение, важно сокращать количество HTTP-запросов, так как одновременно могут скачиваться только несколько ресурсов, а остальные становятся в очередь. Поэтому настраиваем объединение и сжатие стилей и скриптов. В WP подключаем все в functions.php и ставим этот плагин. В Битриксе нужно правильно подключать внешние ресурсы и поставить галочку в настройках главного модуля. Для MODX есть MinifyX.

Сжимать вручную не рекомендуется, потому что такой файл становится неподдерживаемой обузой для сайта и будет только раздуваться с доработками. Еще можно поработать с иконками – объединить их в svg-спрайт, а иконки из шапки (по крайней мере на мобилке) лучше вставить как inline-svg. Так пользователям не придется ждать, пока загрузится файл svg-спрайта.

Для поиска лишних запросов прекрасно подходит вкладка «network» в Chrome. Также можно попробовать использовать cdn для всех ресурсов, например, на WP есть масса плагинов.

До:

После:

Оптимизация картинок 

Что еще тормозит загрузку? Очередь из картинок, которых даже не видно, поэтому обязательно используем lazyload. Другие требования к картинкам на сайте: несколько вариантов изображения (для мобилки и десктопа), поддерживается современный формат и заданы width и height.

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

Сжатие

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

find /dir/www/site.ru -name "*.jpg" -print0 | xargs -0 jpegoptim --all-progressive --strip-all -ft -m85

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

С png все сложнее. Есть готовый скрипт, но он занимает очень много времени, поэтому лучше минимизировать количество png на сайте.

Скрипт для оптимизации:

find /dir/www/site.ru -name "*.png" -print0 | xargs -0 optipng -o7 -strip all -force

Для работы необходим optipng

Конвертация

По требованиям Google все картинки нужно переводить в webp. В целом требование обоснованное, так как в этом формате они весят сильно меньше. На нашей практике, самый удобный способ внедрить webp – через отдачу на сервере. Как это работает? 

  1. Браузер запрашивает у сервера image.jpg.

  2. Сервер смотрит, поддерживает ли браузер webp.

  3. Сервер смотрит, есть ли у него image.webp.

  4. Если все есть, то отдает image.webp вместо image.jpg с необходимым заголовком.

Почему такой способ? Он убирает огромные конструкции из <picture> и @support()позволяет автоматизировать переход на webp.

Как сгенерировать webp-копии всех картинок?

Создаем в корне сайта файл webp-convert.sh. Также для работы нам понадобится установленный cwebp на сервере.

Внутрь вставляем:

#!/bin/bash

# converting JPEG images
find $1 -type f -and \( -iname "*.jpg" -o -iname "*.jpeg" \) \
-exec bash -c '
webp_path=$(sed 's/\.[^.]*$/.webp/' <<< "$0");
if [ ! -f "$webp_path" ]; then
  cwebp -quiet -q 90 "$0" -o "$webp_path";
fi;' {} \;

# converting PNG images
find $1 -type f -and -iname "*.png" \
-exec bash -c '
webp_path=$(sed 's/\.[^.]*$/.webp/' <<< "$0");
if [ ! -f "$webp_path" ]; then
  cwebp -quiet -lossless "$0" -o "$webp_path";
fi;' {} \;

Делаем файл исполняемым, в консоли:

chmod a+x ./webp-convert.sh

А далее запускаем скрипт. Время выполнения зависит от размера сайта.

./webp-convert.sh ./

Для отдачи  webp копий добавляем (для апача) в .htaccess:

<ifModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_ACCEPT} image/webp
  RewriteCond %{REQUEST_URI}  (?i)(.*)(\.jpe?g|\.png)$
  RewriteCond %{DOCUMENT_ROOT}%1.webp -f
  RewriteRule (?i)(.*)(\.jpe?g|\.png)$ %1\.webp [L,T=image/webp,R]
</IfModule>

<IfModule mod_headers.c>
  Header append Vary Accept env=REDIRECT_accept
</IfModule>

AddType image/webp .webp

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

Для nginx отлично описано тут.

Также на некоторых хостингах это включается одной кнопкой, например, на Reddock. 

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

Еще немного об оптимизации картинок: повышение LCP

Если на странице есть баннер в начале, то его обязательно нужно закинуть в <head> в preload. Таким же образом можно подкинуть стили, но лучше не перебарщивать:

<link rel="preload" href="/image.jpg" as="image">

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

Немного вариаций:

Битрикс

В header.php:

<?$APPLICATION->ShowViewContent('PRELOAD');?>

В шаблоне компонента (для страниц, созданных комплексным компонентом) или на индексной странице (для простых страниц):

$image = /* путь до картинки, из arResult или просто строкой */;
$APPLICATION->AddViewContent("PRELOAD", "<link rel='preload' as='image' href='".$image."'>");

Wordpress

Например, тянем из картинки страницы.

В functions.php:

function set_preload() {
    if (has_post_thumbnail() ) {
       $image = get_the_post_thumbnail_url();
        return '<link rel="preload" href="'.$image.'>" as="image">'; 
    }
}

В header.php, в <head>:

<?=set_preload(); ?>

Если все правильно настроено, картинка из preload начинает грузиться сразу после документа:

А в Pagespeed картина такая:

«Удалите неиспользуемый код JavaScript»

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

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

   <script>
		function loadScripts() {
			var script = document.createElement('script');
			script.src = 'https://www.google.com/recaptcha/api.js?render=токен';
			script.async = true;
			document.body.appendChild(script);

			document.removeEventListener("scroll",loadScripts);
			document.removeEventListener("click",loadScripts);
		}
		document.addEventListener("scroll", loadScripts);
		document.addEventListener("click", loadScripts);
</script>

Кстати, гугловскую капчу можно заменить на cloudflare turnstile, она не так требовательна к ресурсам.

По поводу отложенной загрузки метрик или тег-менеджера лучше консультироваться с seo-шником. У него же нужно запросить актуальный список используемых инструментов аналитики, так как на сайт может грузится что-то неиспользуемое, например, facebook pixel.

Подведем короткие итоги

Резюмируем. Чтобы получить быстрый и работающий сайт, нужно:

  1. Стили объединить;

  2. Скрипты тоже объединить, перенести в футер;

  3. Картинки сжать, конвертировать и отложить, svg объединить в спрайт;

  4. Баннер в начале страницы — в preload, иконки из шапки — встроить;

  5. Кэширование на сервере настроить;

  6. Тяжелые внешние скрипты отключить или отложить.

В результате всех манипуляций мы можем получить что-то такое:

Сохраняйте туториал в закладки и делитесь своими советами для джунов в комментариях! Будет здорово, если наш материал станет комплексным сборником полезной инфы по ускорению сайтов. 

Источник: https://habr.com/ru/companies/rocketbusiness/articles/782346/


Интересные статьи

Интересные статьи

Сегодня очень многие backend разработчики стали писать именно rest api. Почему же так? Чем не устраивает обычный django?
При  умеренных объёмах базы данных в использовании offset нет ничего плохого, но со временем база данных растёт и запросы начинают «тормозить». Становится актуальным ускорение запросов.Очеви...
Всем привет. Меня зовут Александр Наумов, я Team Lead QA в Утконос Онлайн. В этой статье я поделюсь личным опытом, который будет полезен тимлидам и руководителям: как мы за 5 месяцев набрали 28 начина...
Обзор возможностей по управлению фрагментацией блобов в Azure Storage Account и ее влияние на скорости доступа к ним.
Сверстать собственный индикатор загрузки — одна из самых простых задач, с которой может столкнуться веб-разработчик. Для получения рабочего решения пригодятся базовые знания HTML и CSS, а JS буде...