В это статье мы расскажем как оптимизировали конкретное Wordpress веб приложение. Какие действия были выполнены чтобы попасть из красной зоны оценки PageSpeed Insights в зеленую, тут будет мало общих рекомендаций универсальных для любых платформ и приложений, которыми пестрит поисковая выдача, a большe описание действий, которые повлияли на результат в рамках конкретной задачи.
PageSpeed Insights — противоречивый инструмент по оптимизации скорости загрузки веб страниц от Google, который за свою семилетнюю историю много раз менял свои алгоритмы, интерфейсы, все время дорабатывался, нещадно хейтился и даже закрывался, но в 2021 году по прежнему более чем актуален и находится в особом почете и уважении у SEO специалистов. И вовсе не потому что является самым объективным и точным, есть много других отличных инструментов 1, 2, 3, а потому что за ним стоит сам “великий и ужасный” Google.
Итак, у нас задача оптимизировать сайт на CMS Wordpress до зеленой зоны.
Сайт несложный с обычной судьбой, был сверстан под нужды заказчика и натянут на wp+woocommerce, в процессе нагружен 40+ плагинами ( что для сайтов на wp обычное дело ).
PSInsights оценивает ваш сайт отдельно для мобильных и десктопных устройств и делит их на три зоны.
- 0–49 — красная
- 50–89 — желтая
- 90–100 — зеленая
Изначально наш сайт имеет такие грустные оценки.
Для тех кто хочет 100/100
Эта статья для вас не подойдет, можно поискать в разделе фантастика, если такой есть на хабре. Современные сайты, а тем более сделанные на CMS обвешаны огромным количеством js и css библиотек, в случае с WP — это почти всегда много плагинов, каждый из которых может что-то добавлять свое на ваш фронт, все это делает сайт огромной неповоротливой махиной и ждать от нее космических скоростей не стоит.
Хотя пустая установка стандартной темы WP twentytwentyone еще выдает желаемую сотню.
Но если помимо этого вы ещё захотите добавить на страницу хоть что-то, то это что-то будет нуждаться в оптимизации.
Этапы оптимизации
- Удалим лишние плагины.
- Поработаем с изображениями.
- Сократим количество js и css на страницах.
- Специфические оптимизации для конкретного проекта.
Удалим лишние плагины
Современный WP это зоопарк различных плагинов, каждый из них это отдельный мир и экосистема. Любой плагин потенциально может добавлять свой функционал на фронт, даже если изначально он задуман только для административной части сайта. Изучать код всех плагинов и оценивать потенциальную угрозу для производительности займет слишком много времени, поэтому действуем от простого к сложному. Удаляем по максимуму все плагины, доводим сайт до нужной зеленой отметки и потом ставим по одному обратно, отслеживая как каждый из них влияет на оценку PSInsights, если влияние присутствует, то думаем над заменой плагина аналогом или отказаться от него.
В нашем случае мы удалили 22 из 40 плагинов и наши цифры чуть повеселели, хотя по прежнему далеки от поставленной цели.
Поработаем с изображениями
Наш проект имеет три разных предупреждения касающихся изображений на сайте.
- Настройте эффективную кодировку изображений.
- Используйте современные форматы изображений.
- Настройте подходящий размер изображений.
К сожалению это три разных проблемы и одной серебряной пули, которая решит все скопом я найти не смог. Поэтому решаем их поэтапно.
Этап 0. Добавим lazy load для изображений
PSInsights оценивает только те изображения, которые смог увидеть, так зачем ему видеть лишнее? Давайте отложим загрузку изображений, которые находятся ниже начальной загрузки экрана.
WP версии 5.4 добавил поддержку lazy load изображений из коробки, в теории просто добавляем атрибут loading="lazy"
к тегу изображений и должно работать, на практике для PSInfights срабатывает не всегда, поэтому ставим плагин autoptimize, который нам еще пригодится ниже для оптимизации js и css кода, и активируем в нем lazy load загрузку изображений.
Этап 1. Настройте эффективную кодировку изображений
По факту это значит что изображения недостаточно оптимизированы.
У PSInsights свой алгоритм по которому он определяет что такое достаточно. К сожалению, все топовые плагины оптимизации изображений до конца не могут оптимизировать изображение под их алгоритм.
Я воспользовался этим решением, оно позволяет в бесплатной версии оптимизировать все изображения сразу по нажатию одной кнопки. У нас еще могу остаться предупреждения про кодировку после этого, но вес основной части изображений будет меньше.
Этап 2. Используйте современные форматы изображений
Тут все не так просто, взять и заменить форматы изображений на webp скопом не получиться, поддержка этого формата браузерами все еще оставляет желать лучшего.
Для конвертации всех изображений в webp формат я воспользовался этим плагином, который в папке uploads рядом с существующими изображениями создает новые с расширением webp. То есть, если раньше у вас было изображение foo.jpeg, то сейчас рядом с ним появилось foo.jpeg.webp
В теории в таких плагинах есть настройка, которая сама подменяет изображения на webp и показывает нужный формат в зависимости от браузера, на практике у меня предупреждения про формат все равно остались даже после активации настройки.
Поэтому, в тех местах где lazy load не смог скрыть изображения, я вручную заменил тег img на тег picture, как это работает хорошо описано тут.
Код замены привожу ниже, все php переменные естественно должны быть объявлены перед тегом.
<picture>
<source srcset="<?php echo $src . '.webp'; ?>" type="image/webp">
<source srcset="<?php echo $src; ?>" type="image/jpeg">
<img loading="lazy" src="<?php echo $src; ?>" alt="<?php echo $alt; ?>" width="<?php echo $width; ?>" height="<?php echo $height; ?>">
</picture>
Этап 3. Настройте подходящий размер изображений
Это предупреждение появляется если вы пытаетесь загрузить изначально очень большое изображение, а показываете его как маленькое. Тут опять действовать придется точечно и в тех местах где lazy load не смог скрыть изображения, пробовать подключать другие размеры картинок из существующих.
На практике может оказаться так, что существующих недостаточно, тогда придется еще создавать свои отдельные. Для этого объявляем новый размер в functions.php вашей темы.
add_action( 'init', 'frank_theme_init' );
function frank_theme_init() {
add_image_size( 'post_slider_thumbnail', '517', '354', true );
add_image_size( 'post_card_slider_thumbnail', '400', '370', true );
}
Затем любым плагином, который может ресайзить изображения, создаем новые размеры и подгружаем их в тег picture, который вы вставляли выше.
После проведенных оптимизаций с изображениями, наши балы заметно повеселели, а для десктопа мы уже попали в желаемую зеленую зону.
Переходим к следующему этапу оптимизации.
Сократим количество js и css на страницах
У нас осталась группа рекомендаций по оптимизации скорости загрузки, все из них относятся к js и css файлам.
- Уменьшите размер кода JavaScript
- Удалите неиспользуемый код CSS
- Удалите неиспользуемый код JavaScript
- Устраните ресурсы, блокирующие отображение
Как видим основные рекомендации это размер js и css библиотек. Нужно определить кто эти библиотеки нам на фронт добавляет. Открываем вкладку network панели хрома, выбираем отображение только js файлов, затем обновляем страницу через ctrl + F5. После этого сортируем по размеру файлов, чтобы выявить самые тяжелые, от которых потенциально исходит самая большая угроза.
В итоге видим что самые тяжелый файл нам добавляют:
- Плагин оператора сайта
- Сервисы статистики и аналитики ( facebook pixel, google tag manager)
- Стандартные библиотеки (jquery, bootstrap)
К сожалению лишние тут выявить сложно, современный веб ресурс сложно представить без этих сервисов, но тем не менее что-то мы должны предпринять.
Отключаем плагин оператора, он добавляет критично много, его в дальнейшем на продакшене либо заменим на другой, либо добавим таймаут на загрузку.
Следующий потенциальный враг это фейсбук пиксель, добавляем таймаут на его загрузку, да наверное это может сказаться на объективности его данных в дальнейшем, но тут уже нужно искать компромисс, что вам больше нужно, скорость загрузки ресурса или объективность данных этого сервиса.
Также отключаем всякие мелочи по типу эмоджи wp, которыми мы не пользуемся на нашем фронте
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );
После этого останется устранить ресурсы блокирующие отображение.
Если все ваши стили и скрипты в теме и плагинах подключены правильно через wp_enqueue_style и wp_enqueue_script соответственно, то устранить блокировку поможет ранее установленный плагин autoptimize, просто активируйте соответствующие галочки.
После этого может возникнуть проблема с отображением ресурса в первые секунды загрузки, для этого нужно критично важный css отображать выше. В нашем случае мы просто возмем из bootstrap.css часть стилей отвечающих за отображение сетки и добавим их в соответствующее окно настроек.
Под капотом плагин соберет все подключаемые файлы js и css в два больших файла, минифицирует их и отдаст на фронт.
После оптимизаций связанных с js и css наши цифры стали совсем близки к цели.
У нас 80+ для мобильных, и стабильно зеленая зона для десктопа.
Предупреждения по сокращению размеров js и css кода по прежнему висят, но их мы больше оптимизировать не будем, потому-что это будет очень трудозатратно по времени, и не нужно в рамках нашей задачи.
Остался последний рывок и тут уже не обойтись без специфических оптимизаций для конкретного проекта.
Специфические оптимизации для конкретного проекта
На самом деле такие спец оптимизации мы уже приводили выше, все проекты уникальны по своему и в вашем ошибки выдаваемые PSInsights могут существенно отличаться, тогда вам нужно вырабатывать свой алгоритм их решения.
В нашем случае для финальной оптимизации мы обратим внимание на результаты “Имитация загрузки страницы”, эти параметры очень важны. Нужно понимать что вы можете выполнить все рекомендации из раздела “Оптимизация” и “Диагностика”, но если при этом параметры имитации загрузки страницы остаются в красной зоне, то высоких оценок вы все равно не получите. Ваш сайт должен стать реально быстрее в процессе оптимизации, согласитесь что это довольно справедливое требование.
Итак в рамках нашего проекта мы видим, что одним из факторов отмеченных красным является “Largest content paint”
Логически глядя на страницу можно легко догадаться какой элемент является самым большим на странице, в нашем случае есть подозрение, что это верхний слайдер запускаемый slick библиотекой. Но мы не будем полагаться только на интуицию, а доверимся инструментам. Открываем панель хрома, вкладку “Performance” и запускаем тест, затем кликаем по кладке “LCP”. В итоге виновник найден. Подозрения подтвердились — это наш верхний слайдер.
При загрузке страницы юзеру неважно слайдером будет наше первое изображение изначально или картинкой. Поэтому для решения проблемы мы заменяем слайдер на картинку, ждем полной загрузки страницы и затем запускаем скрипт инициализации слайдера, а после этого прячем изображение.
В коде это будет выглядеть примерно так. Добавляем параллельно c html слайдера тег изображения и вставляем в его src первое изображение слайдера
$post_thumbnail_id = $product->get_image_id();
$image = franc_get_appropriate_size_image_downsize($post_thumbnail_id, 'large');
$attachment_url = $image[0];
$image = image_downsize( $post_thumbnail_id, 'full' );
$alt = get_post_meta($post_thumbnail_id, '_wp_attachment_image_alt', true);
if ( empty($alt) ) {
$alt = __('product image', 'Frank_Maisler');
}
?>
<div class="preload-single-product-image">
<?php
$args = [
'src' => $image[0],
'alt' => $alt,
'width' => $image[1],
'height' => $image[2],
'class' => 'img-fluid',
];
frank_show_template('templates/parts/picture-tag', $args);
?>
</div>
<div class="slider single-product-slider-image slider-for d-none">
<div>
<img loading="lazy" alt="<?php echo $alt; ?>" src="<?=$attachment_url?>">
</div>
<?php
$attachment_ids = $product->get_gallery_image_ids();
foreach( $attachment_ids as $attachment_id ) {
$alt = get_post_meta($post_thumbnail_id, '_wp_attachment_image_alt', true);
if ( empty($alt) ) {
$alt = __('product image', 'Frank_Maisler');
}
?>
<div>
<img loading="lazy" alt="<?php echo $alt; ?>" src="<?php echo wp_get_attachment_url( $attachment_id ); ?>">
</div>
<?php
}
?>
</div>
<div class="slider slider-nav single-product-slider-carousel d-none">
<div>
<img loading="lazy" alt="<?php echo $alt; ?>" src="<?=$attachment_url?>">
</div>
<?php
foreach( $attachment_ids as $attachment_id ) :
$alt = get_post_meta($post_thumbnail_id, '_wp_attachment_image_alt', true);
if ( empty($alt) ) {
$alt = __('product image', 'Frank_Maisler');
}
?>
<div>
<img loading="lazy" alt="<?php echo $alt; ?>" src="<?php echo wp_get_attachment_url( $attachment_id ); ?>">
</div>
<?php
endforeach;
?>
</div>
Затем в js помещаем инициализацию слайдера в таймаут и скрываем изображением после инициализации
jQuery(document).ready(function($) {
setTimeout(function() {
if ( jQuery('.single-product-slider-carousel img').length != 1) {
jQuery('.single-product-slider-image').removeClass('d-none');
jQuery('.single-product-slider-carousel').removeClass('d-none');
jQuery('.slider-nav').slick({
lazyLoad: 'ondemand',
slidesToShow: 4,
slidesToScroll: 1,
arrows:false,
asNavFor: '.slider-for',
focusOnSelect: true
});
setTimeout(function() {
var main_image = jQuery('.preload-single-product-image'),
height = main_image.height();
jQuery(".single-product-slider-image").height(height);
main_image.addClass('d-none');
}, 1000);
}
}, 5000);
});
После этих оптимизаций юзер видит при загрузке вначале изображение, a потом через время слайдер. PSInsights видит только изображение, которое грузится гораздо быстрее чем весь слайдер. В итоге все счастливы и наши балы оценки сияют зелеными цифрами.
Подведем итоги. Задача выполнена, все страницы нашего проекта в зеленой зоне. Конечно еще можно провести много всякий оптимизаций:
- Сократить bootstrap.css оставив только нужные элементы.
- Попрофилировать ресурс в поисках узких мест загрузки php и sql запросов.
- Настроить более эффективно кэширование.
И еще много чего, но на сегодня цель достигнута, сайт действительно стал грузиться быстрее и PSInsights оценивает его высоко.
vitaly_il1
Спасибо, интересный пример!