Погоня за увеличением скорости загрузки страниц продолжается. Представим, что мы всё сжали, минифицировали, объединили в один файл, скрипты "опустили", используем svg-спрайты и всё в этом духе. Что дальше? Одно из трендовых инноваций сегодня (особенно актуально на мобильниках), это размещение стилей верхней части страницы прямо в тег head и асинхронная подгрузка остальных файлов. Данную рекомендацию сплошь и рядом пропагандируют все сервисы тестирования производительности, в том числе и гугловый, так как браузер не начнёт рендерить страницу и не покажет её пользователю, пока не получит все linkи в head. Статья на тему.
В своём примере я не выделяю стили верхней части страницы, а подключаю весь закешированный битриксом файл стилей в теге head. Хочу сразу предупредить, что данный метод стоить тестировать на производительность на конкретном сайте, в некоторых случаях это может увеличить First byte из-за работы с большими строками через регулярные выражения, в некоторых сильно увеличит объём страницы и уменьшит объём кешируемых данных, что приведёт к обратному эффекту.
/* Для замера скорости работы */
$start = microtime(true);
/* Получить пути подключенных файлов стилей */
$css_str = Bitrix\Main\Page\Asset::getInstance()->GetCSS();
preg_match_all('/href="(.*)"\stype/', $css_str, $matches);
/*
* Перебираем результат, так как метод возвращает все подключаемые файлы стилей
* нас интересует только кеш объединённых файлов, содержит в названии 'template_'
*/
foreach($matches[1] as $val)
{
$val = explode('?', $val);
$full_path = $_SERVER['DOCUMENT_ROOT'] . $val[0];
/*
* Производим подмену только для закешированного объединённого файла
* если он существует и не пуст (на пустоту проверка дальше)
*/
if (strpos($full_path, 'template_') !== false && file_exists($full_path))
{
$st yle = trim( file_get_contents($full_path) );
if (!empty($style))
{
$search = '<st yle type="text/css"></style>';
$replace = '<st yle type="text/css">'. $style .'</style>';
// Вставить стили
$content = str_replace($search, $replace, $content);
// Вырезить <li nk>
$content = preg_replace('/<li nk\shref="'. addcslashes($val[0], '/') .'(.*)>/', '', $content);
}
}
}
/* Вставляем время исполнения кода в заранее подготовленное место */
$content = str_replace('#time_replace#', microtime(true) - $start, $content);
Немного истории. Bitrix Framework буфферизирует весь клиентский код. Данный подход делает возможным работу отложенных функций и прочей манипуляции с содержимым сайта «выше», после того как мы находимся уже совершенно в другой части исполнения страницы и не имеем возможности перепроектировать последовательность под конкретный случай.
В своём решении я использую событие OnEndBufferContent (вызывается при выводе буфферизированного контента), у него имеется один аргумент $content – тот самый буфферизированный контент, который мы будем править.
Идеязаключается в том, чтобы найти подключенный закешированный (объединённый и минифицированный) файл стилей, получить его содержимое, вырезать его подключение из кода и вставить содержимое в теге <style> в head. Маркер для вставки в виде пустого блока стилей, на случай если что-то пойдёт не так, страница останется целой и валидной:
<st yle type="text/css"></style>
Метод будет работает стабильно всегда, так как получает имя сгенерированного битриксом файла через специальный api-метод, ищет его в контенте и ещё раз проверяет файл по имени (содержит template_), проверяет его существование на сервере и если он не пустой, вставляет в документ и очищает контент от link’a только вставленного файла.
Результат можно посмотреть здесь, не обещаю, что будет всегда включено Ещё, кстати, убираю css-комментарии и лишние переносы, это просто по фану. Не рекомендую сильно заморачиваться, также не рекомендую заморачиваться по поводу вырезания пробелов или ещё какой-то манипуляции с контентом, так как не стоит забывать, что html-результат сжимается gzip’ом, по хорошему конечно Но когда вы дойдёте до применения вставки стилей в head, то всё остальное по умолчанию у вас будет по феншую.
/*
* Убрать комментарии из стилей и лишние переносы
* можно применить как ко всему контенту, так и только к файлам стилей, но лучше вообще не заморачиваться :)
*/
$arReplace = array(
"/\\/\\*(.*)\\*\\//" => "",
"/\n+/" => "\n",
);
$content = preg_replace(array_flip($arReplace), $arReplace, $content);
Метод протестирован с объединёнными файлами стилей и без, со сжатыми, минифицированными и без. Даже если вдруг произойдёт вставка не того файла, то только его подключение и будет вырезано, но такого замечено не было. Ещё по поводу времени исполнения, решайте сами, что для вас быстро или медленно, смотрите как это влияет на общую скорость отработки страницы в вашем случае. У меня это ~0,0009, но и контента на приведённом в примере сайте очень мало. Также не стоит забывать, что подключенные внешние файлы стилей будут кешироваться браузером и такой прямой подход как здесь увеличит объём страницы и уменьшит кол-во кешируемых данных, так что подходит не везде. Если у вас не много стилей, маленький или среднего размера проект, то скорее всего вы получите прирост. На крупных проектах метод не стоит использовать или стоит дорабатывать.
Ремарка про шрифты,
var resource = document.createElement('link');
resource.setAttribute("rel", "stylesheet");
resource.setAttribute("href","https://fonts.googleapis.com/css?family=Open+Sans+Condensed:700|Open+Sans:400,600&subset=cyrillic,cyrillic-ext");
var head = document.getElementsByTagName('head')[0];
head.appendChild(resource);
Код асинхронно подгружает файлы шрифтов, подключаемых с fonts.googleapis.com. Но это создаёт одну неприятность. После загрузки шрифта, если в этот момент сайт уже отрендерин, страница заметно моргнёт. Проблему наблюдал в chrome, решение искать не стал и от идеи отказался, мигание ушло. Если вас ваше руководство/клиента это устроит, используйте, гугл прибавит вам пару «попугаев» к тесту. Если кому известно в чём причина и как исправить, пишите в комментариях. Наличие или отсутствие data-skip-moving на результат не влияет. При повторном открытие стр. проблемы нет, так как файл шрифтов уже в кеше браузера.
Самый правильный способ такой (ИМХО): 1. Грамотно верстаем. Выделяем стили, отвечающие за скелет страницы. 2. Все эти стили в хеадер пихаем. Можно сделать проверку по кукам и сессии и только при первом посещении в хеадере выдавать эти стили. Причем они же должны дублироваться в основных файлах стилей, которые закэшируются браузером и не будут грузиться второй раз.
Но по оптимизации гораздо больше работ, чем просто стили подключить. 1. Если шрифты неприятно дергают страницу после загрузки - подбираем штатный шрифт с похожими размерами. Тогда при загрузке шрифта разница будет не столь заметна. 2. Грузим шрифты, например, с гугла, если это популярные бесплатные шрифты. Тогда есть вероятность, что у пользователя в браузере уже есть нужный закэшированый скрипт. 3. Не забываем про изображения. Это главный бич на данный момент. Гуглспид стал анализировать всю страницу, а не только верхнюю часть. А для всей сраницы гораздо сложнее заставить грузить оптимизированный контент - поэтому юзаем ленивую загрузку. 4. Ленивая загрузка частично решает проблему картинок для гуглспида, но не решает проблему трафика. a) Битрикс штатно не умеет подсовывать изображения нужного разрешения (да и не должен уметь в общем-то). В итоге грузим банер для разрешения в 2560px шириной и потом все это отображаем на мобильном телефоне. б) Если вы думаете, что скрыв блок баннеров адаптивом (display: none) проблема решается - я вас разочарую. Браузеры грузят это все, чтобы "сайт работал быстро" (раньше мы для этого грузили нужные картинки в блок 1*1пиксел). в) Та же беда с обычными картинками по контенту, только решается чуть сложнее, чем с баннерами. г) И, да. PHP не умеет качественно жать картинки. Гугл все равно ткнет носом, что можно бы ещё несколько кб с каждой картинки срезать, даже если вы уже поставили сжатие "хуже некуда"
Ага, достаточно выделить grid и цвета, фоны элементов.
Sergey Emelyanov написал: 2. Все эти стили в хеадер пихаем. Можно сделать проверку по кукам и сессии и только при первом посещении в хеадере выдавать эти стили. Причем они же должны дублироваться в основных файлах стилей, которые закэшируются браузером и не будут грузиться второй раз.
Куку будет сложно с композитом подружить, особенно, если композитный кеш отдается ngix-ом.
Точно, про композит забыл. Теоретически, если грамотно выделить нужные стили - это 2-3 кб дополнительно к странице (если жмется сервером страница и того меньше). Думаю это не столь критично.
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».