+

PageSpeed Insights, шта с тобой случилось?

20.11.2018 01:43
21.11.2018 04:27
4446

Доброго времени суток %USER_NAME%! В этом материале ты найдешь краткие инструкции по оптимизации скорости загрузки сайта, дабы пройти PageSpeed Insights!

Начнём мы с самого важного, подбора музыки, на этот раз это классика..

PageSpeed Insights уже не тот

PageSpeed Insights старый добрый инструмент от google для оценки качества сайтов и web-приложений.
12 ноября на него накатили мощьное обновление, которое коснулось не только дизайна самого инструмента но и нервов обладателей сайтов! Ведь это обновление наглядно показывает что пока мы живём в 2018 гугл уже в 2666 где на замену кожаным ублюдкам пришли роботы! Или это просто какой то троллинг, так как многие разработчики пишут что зделанные изменения не увеличивают оценку скорости загрузки, либо гугл далеко не сразу замечает изменения.

Замер скорости теперь производится на основе Lighthouse и отчета о пользователях Chrome (CrUX).

Важно понимать что этот сервис еще обкатывает обновления и периодически выдаёт недоставерную информацию, бывает что при нескольких замерах подряд он выдаёт разброс до 20 балов, а иногда и вовсе не замеряет скорость а выдаёт ошибку backend error. У меня такое было с одним сайтом, помощи в гугл не нашёл, заработало после того когда я убрал пару строчек js кода отвечающего за анимацию загрузки перед появлением контента сайта.
В таких случаях можно пользоватся Lighthouse через браузер chrome, там он работает стабильно.

Lighthouse* Lighthouse, находится в инструментах разработчика (ctrl + alt + i или f12)

Изображения

Используйте современные форматы изображений - говорит инструмент замерки, а это форматы JPEG 2000, JPEG XR, WebP, и тут важно понимать что некоторые пользователи используют браузеры которые не поддерживают ничего современного, по этому выберем формат с наибольшей поддержкой среди браузеров, лучше всех с этим справляется WebP, даже Edge в октябре 2018 начал его поддерживать, но к сожалению IE и Safary не знают о его существовании, с ними разберёмся позже.

Для конвертации и сжатия картинок в webp я использовал онлайн приложение image.online-convert.com, в photoshop к сожалению для сохранения изображений в webp нужно дополнительно приложение, zeplin этого тоже не может, вроде как avocode может.

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

Оригинальное изображение

Оригинал, png, 36.4кб

Сжатие при помощи tinypng

Зажимаем при помощи
tinypng 29.8кб

Формат webp

webp 14.2кб

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


  <picture>
    <source srcset="img/articles/9m.webp" type="image/webp">
    <img src="img/articles/9m_tinypng.png" alt="Alt Text!">
  </picture>

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

Alt Text!

Теперь перейдём к стилям, как определить что браузер поддерживает webp для CSS? Для этого нужно использовать JS, а конкретно плагин Modernizr, на сайте этого плагина мы можем выбрать для скачивания только необходимые нам функции, отслеживание поддержки webp, через поиск на странице находим всё связанное с webp и добавляем в сборку, скачиваем и прикручиваем на сайт.

Теперь у нас появляется специальные классы у body, .no-webp и .webp на основе чего мы можем писать следующие стили

.no-webp .elementWithBackgroundImage {
  background-image: url("img/articles/9m_tinypng.png");
}

.webp .elementWithBackgroundImage{
  background-image: url("img/articles/9m.webp");
}

К счастью гугл не жалуется на формат SVG

Ну и для фула, проверяем включен ли JS на сайте, добавляем класс no-js к html

<html class="no-js">

И скрипт для проверки включенного JS


  <script>
    document.documentElement.classList.remove("no-js");
  </script>

На заметку, проверка поддержки браузером webp с помощью php

if( strpos( $_SERVER['HTTP_ACCEPT'], 'image/webp' ) !== false ) {
    // webp is supported!
}

А также определение браузера с помощью JS, webp хорошо работает в Chrome и соответственно в Opera

function GetBrowserAgentName($user_agent) {
    if (strpos($user_agent, 'Opera') || strpos($user_agent, 'OPR/')) return 'Opera';
    elseif (strpos($user_agent, 'Edge')) return 'Edge';
    elseif (strpos($user_agent, 'Chrome')) return 'Chrome';
    elseif (strpos($user_agent, 'Safari')) return 'Safari';
    elseif (strpos($user_agent, 'Firefox')) return 'Firefox';
    elseif (strpos($user_agent, 'MSIE') || strpos($user_agent, 'Trident/7')) return 'Internet Explorer';
    return 'Other';
}
Про отложенную загрузку можно почитать тут

Подключение файлов (js, css, html)

Тут есть большое изменение, раньше большинство разработчиков стремились для оптимизации скорости склеить все js файлы в один, как и css, теперь надо наоборот разбивать на файлы в зависимости от важности и устройства на котором открывается сайт.
Если эти правила не будут соблюдены замерщик посчитает это ошибкой и определит подключаемые файлы как блокирующие отображение.

Теперь подробнее

Рекомендуется подключать самые важные влияющие на отрисовку и структуру контента файлы js и css в теге head следующим образом

  <head>
    ...
    <link rel="preload" href="styles.css" as="style">
    <link rel="preload" href="ui.js" as="script">
    ...
  </head>

Остальные подключаемые файлы должны соответствовать следующим требованиям

блокирующим отображение <script> тег или JS файл становится если:

* Атрибут async поддерживается всеми браузерами, кроме IE9, находя скрипт с таким атрибутом браузер будет загружать страницу дальше, а скрипт будет загружатся на фоне, когда он будет загружен то выполнится, не зависимо какой он идёт по очереди.
* Атрибут defer поддерживается всеми браузерами, скрипт тоже подгружается на фоне, но соблюдается порядок подключения скриптов с таким атрибутом, скрипт будет выполнен после обработки всего документа.

блокирующим отображение <link rel="stylesheet"> тег становится если:

блокирующим отображение <link rel="import"> тег становится если:

Для сжатия эти файлов рекомендую использовать программу Koala.

Подключение шрифтов

Параметр метрики "Время загрузки для взаимодействия" зависит от того как быстро сайт перестал дёргатся и менять размеры блоков, замер этого параметра происходит дополнительно по скриншотам сделанным на протяжения загрузки старницы, их вы можете увидеть в результате теста.
Из чего мы собственно и получаем проблему со шрифтами (и не только), Настройте показ всего текста во время загрузки веб-шрифтов, тоесть пока подгружаются шрифты страница пляшет, исправить это можно следующими настройками

В код подключения шрифта надо добавить параметр font-display который может иметь следующие значения:

Судя по всему замерщик ждёт от нас swap, дадим ему его.

@font-face {
  font-family: 'Arvo';
  font-display: swap;
  src: local('Arvo'), url(https://fonts.gstatic.com/s/arvo/v9/rC7kKhY-eUDY-ucISTIf5PesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
}

Раз уж мы добрались до шрифтов, разсмотрим современные форматы для сжатия шрифтов.

Самый софременный формат в вебе это woff2, вооружившись сервисом сжатия font-converter.net мы сжимаем шрифты, и вот что выходит

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

Результат не очень, так как на лицо, качество шрифта упало, но вот woff не потерял качества и жикор скинул не плохо.

Шрифт до сжатия в формате otf

otf 222кб

Шрифт woff от сжатия otf

woff 121кб

Шрифт woff2 от сжатия otf

woff2 73кб

Кэширование файлов

Даже если заданы параметры кэширования картинок и скриптов замерщик гугла смотрит на сайт как на говно, почему? Потому что нужно больше времени кэшировани! От месяца до года!
И тут я столкнулся с проблемой, хостинг beget как и многие использует связку nginx и apache, nginx обрабатывает статистические файлы, например js и css, и время жизни кэша Cache-Control указывается в его параметрах а не в .htaccess для apache, доступ к редактированию параметров nginx закрыт, благо beget замечательный хостинг, после переписки с тех поддержкой они перенаправили обработку js и css на Apache. Ошибки кэша пропали но добавилось требование добавить сжатие этих файлов при помощи gzip, deflate или brotli.

В итоге добавляем в .htacces следующий код

# Enable Compression
  <IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE application/rss+xml
    AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
    AddOutputFilterByType DEFLATE application/x-font
    AddOutputFilterByType DEFLATE application/x-font-opentype
    AddOutputFilterByType DEFLATE application/x-font-otf
    AddOutputFilterByType DEFLATE application/x-font-truetype
    AddOutputFilterByType DEFLATE application/x-font-ttf
    AddOutputFilterByType DEFLATE application/x-javascript
    AddOutputFilterByType DEFLATE application/xhtml+xml
    AddOutputFilterByType DEFLATE application/xml
    AddOutputFilterByType DEFLATE font/opentype
    AddOutputFilterByType DEFLATE font/otf
    AddOutputFilterByType DEFLATE font/ttf
    AddOutputFilterByType DEFLATE image/svg+xml
    AddOutputFilterByType DEFLATE image/x-icon
    AddOutputFilterByType DEFLATE text/css
    AddOutputFilterByType DEFLATE text/html
    AddOutputFilterByType DEFLATE text/javascript
    AddOutputFilterByType DEFLATE text/plain
  </IfModule>
  <IfModule mod_gzip.c>
    mod_gzip_on Yes
    mod_gzip_dechunk Yes
    mod_gzip_item_include file .(html?|txt|css|js|php|pl)$
    mod_gzip_item_include handler ^cgi-script$
    mod_gzip_item_include mime ^text/.*
    mod_gzip_item_include mime ^application/x-javascript.*
    mod_gzip_item_exclude mime ^image/.*
    mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
  </IfModule>
  
  # Leverage Browser Caching
  <IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpg "access 1 year"
    ExpiresByType image/jpeg "access 1 year"
    ExpiresByType image/gif "access 1 year"
    ExpiresByType image/png "access 1 year"
    ExpiresByType image/webp "access 1 year"
  	ExpiresByType application/octet-stream "access 1 year"
  	ExpiresByType application/x-font-otf "access 1 year"
    ExpiresByType text/css "access 1 month"
    ExpiresByType text/html "access 1 month"
    ExpiresByType application/pdf "access 1 month"
    ExpiresByType text/x-javascript "access 1 month"
    ExpiresByType application/x-shockwave-flash "access 1 month"
    ExpiresByType image/x-icon "access 1 year"
    ExpiresDefault "access 1 year"
  </IfModule>
  <IfModule mod_headers.c>
  <filesmatch "\.(ico|flv|jpg|jpeg|webp|png|gif|css|swf)$">
  	Header set Cache-Control "max-age=31536000, public"
  </filesmatch>
  <filesmatch "\.(html|htm)$">
  	Header set Cache-Control "max-age=7200, private, must-revalidate"
  </filesmatch>
  <filesmatch "\.(pdf)$">
  	Header set Cache-Control "max-age=86400, public"
  </filesmatch>
  <filesmatch "\.(js|otf|ttf|woff|woff2)$">
  	Header set Cache-Control "max-age=31536000, private"
  </filesmatch>
  </IfModule>

Сократите глубину вложенности критических запросов

Не смотря на все попытки, в этом списке остаются подключаемые шрифты. Файлы стилей можно исключить из этого списка выкатив их содержимое в тег style расположенный в head. как вариант можно попробовать добавить атрибут rel="preload", не проверял

Метки и промежутки пользовательского времени (User Timing API)

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

Этот инструмент содержит 2 основных типа методов, одни для внесения данных в замеры, другие для их получения.

Пример замера:

performance.mark("startWork");
doWork(); // Код для замера
performance.mark("endWork");

Получение результатов:

var items = performance.getEntriesByType('mark');
console.log(items)

Как правило один такой замер уберёт этот пункт из PageSpeed Insights

Итог

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