Погоня за увеличением скорости загрузки страниц продолжается. Представим, что мы всё сжали, минифицировали, объединили в один файл, скрипты "опустили", используем svg-спрайты и всё в этом духе. Что дальше? Одно из трендовых инноваций сегодня (особенно актуально на мобильниках), это размещение стилей верхней части страницы прямо в тег head и асинхронная подгрузка остальных файлов. Данную рекомендацию сплошь и рядом пропагандируют все сервисы тестирования производительности, в том числе и , так как браузер не начнёт рендерить страницу и не покажет её пользователю, пока не получит все linkи в head. .
В своём примере я не выделяю стили верхней части страницы, а подключаю весь закешированный битриксом файл стилей в теге head. Хочу сразу предупредить, что данный метод стоить тестировать на производительность на конкретном сайте, в некоторых случаях это может увеличить First byte из-за работы с большими строками через регулярные выражения, в некоторых сильно увеличит объём страницы и уменьшит объём кешируемых данных, что приведёт к обратному эффекту.
Немного истории. Bitrix Framework буфферизирует весь клиентский код. Данный подход делает возможным работу отложенных функций и прочей манипуляции с содержимым сайта «выше», после того как мы находимся уже совершенно в другой части исполнения страницы и не имеем возможности перепроектировать последовательность под конкретный случай.
В своём решении я использую событие OnEndBufferContent (вызывается при выводе буфферизированного контента), у него имеется один аргумент $content – тот самый буфферизированный контент, который мы будем править.
Идея заключается в том, чтобы найти подключенный закешированный (объединённый и минифицированный) файл стилей, получить его содержимое, вырезать его подключение из кода и вставить содержимое в теге <style> в head. Маркер для вставки в виде пустого блока стилей, на случай если что-то пойдёт не так, страница останется целой и валидной:
Метод будет работает стабильно всегда, так как получает имя сгенерированного битриксом файла через специальный api-метод, ищет его в контенте и ещё раз проверяет файл по имени (содержит template_), проверяет его существование на сервере и если он не пустой, вставляет в документ и очищает контент от link’a только вставленного файла.
Результат можно посмотреть , не обещаю, что будет всегда включено
Ещё, кстати, убираю css-комментарии и лишние переносы, это просто по фану. Не рекомендую сильно заморачиваться, также не рекомендую заморачиваться по поводу вырезания пробелов или ещё какой-то манипуляции с контентом, так как не стоит забывать, что html-результат сжимается gzip’ом, по хорошему конечно
Но когда вы дойдёте до применения вставки стилей в head, то всё остальное по умолчанию у вас будет по феншую.
Метод протестирован с объединёнными файлами стилей и без, со сжатыми, минифицированными и без. Даже если вдруг произойдёт вставка не того файла, то только его подключение и будет вырезано, но такого замечено не было. Ещё по поводу времени исполнения, решайте сами, что для вас быстро или медленно, смотрите как это влияет на общую скорость отработки страницы в вашем случае. У меня это ~0,0009, но и контента на приведённом в примере сайте очень мало.
Также не стоит забывать, что подключенные внешние файлы стилей будут кешироваться браузером и такой прямой подход как здесь увеличит объём страницы и уменьшит кол-во кешируемых данных, так что подходит не везде. Если у вас не много стилей, маленький или среднего размера проект, то скорее всего вы получите прирост. На крупных проектах метод не стоит использовать или стоит дорабатывать.
Ремарка про шрифты,
Код асинхронно подгружает файлы шрифтов, подключаемых с fonts.googleapis.com. Но это создаёт одну неприятность. После загрузки шрифта, если в этот момент сайт уже отрендерин, страница заметно моргнёт. Проблему наблюдал в chrome, решение искать не стал и от идеи отказался, мигание ушло. Если вас ваше руководство/клиента это устроит, используйте, гугл прибавит вам пару «попугаев» к тесту. Если кому известно в чём причина и как исправить, пишите в комментариях. Наличие или отсутствие data-skip-moving на результат не влияет. При повторном открытие стр. проблемы нет, так как файл шрифтов уже в кеше браузера.
UPD: рабочий код на
В своём примере я не выделяю стили верхней части страницы, а подключаю весь закешированный битриксом файл стилей в теге 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);
|
В своём решении я использую событие OnEndBufferContent (вызывается при выводе буфферизированного контента), у него имеется один аргумент $content – тот самый буфферизированный контент, который мы будем править.
Идея заключается в том, чтобы найти подключенный закешированный (объединённый и минифицированный) файл стилей, получить его содержимое, вырезать его подключение из кода и вставить содержимое в теге <style> в head. Маркер для вставки в виде пустого блока стилей, на случай если что-то пойдёт не так, страница останется целой и валидной:
<st yle type="text/css"></style> |
Результат можно посмотреть , не обещаю, что будет всегда включено

Ещё, кстати, убираю 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);
|
UPD: рабочий код на