Умное объединение стилей и скриптов
Каждый, кто задавался вопросом оптимизации работа сайта (по-большому счёту, — все) и применял битриксовский механизм объединения css- или js-файлов (далее — ассетсов), при анализе результата не мог быть удовлетворён полностью. Процесс объединения не поддаётся явному контролю, результат объединения может измениться по непонятным для нас причинам. При чём этот подход — сваливание в общую кучу всех ассетсов — явно не то, чего мы хотели бы получить: в любом проекте есть «зафиксированные» ассетсы (по которым работа не ведётся) и не хочется, чтобы на их кеш влияли новые, которые только находятся в работе. Давайте разберёмся в особенностях механизма (речь про чекбоксы «Объединять CSS файлы» и «Объединять JS файлы», в настройках Главного модуля) и посмотрим, на что мы можем повлиять.
Претензии, с которыми мы будем разбираемся:
- Мне не нравится, что добавление нового компонента в каком-то месте сайта, приводит к перегенерации уже скомпилированного кеш-файла.
- Мне не нравится, что они смешаны в кучу. Я хочу понять, по какому принципу механизм решает, какие ассетсы в какой кеш-файл определить.
- Зная, какие ассетсы не будут меняться в обозримом будущем (к примеру, стили каркаса сайта), я хотел бы чтобы они объединялись в отдельный кеш-файл и никто из компонентов их не перегенеривал.
¶1. Стандартные потоки ассетсов.
В контексте обсуждаемого вопроса, в Битриксе есть понятие целевых потоков ( \Bitrix\Main\Page\Asset::$targetList ). Главные, с которыми мы имеем дело: ассетсы ядра, шаблона, страницы.
¶1.1. Ассетсы ядра
Код целевого потока: KERNEL — все, которые лежат в путях /bitrix/js/ , /bitrix/css/ .
Особенность: на них механизм объединения не действует.
Здесь же: аналогично не действует объединение для тех ассетсов, которые расположены в путях:
¶1.2. Ассетсы шаблона
Код целевого потока: TEMPLATE — те ассетсы, которые были добавлены во время выполнения кода, инициированного из файлов шаблона сайта: header.php и footer.php . Ещё раз, это важно: если у нас начал подключаться и выполняться header.php нашего шаблона сайта и внутри него был выполнен компонент и компонент добавил в общий список какой-то ассетс, этот ассетс детектится как относящийся к пространству TEMPLATE.
Признак: кеш-файл, подключенный в итоговый html-кода страницы, имеет в пути вкрапление template_, пример:
/bitrix/cache/css/>/>/template_e4f06/template_e4f06_v1.css
¶1.3. Ассетсы страницы
Код целевого потока: PAGE — те ассетсы, которые были добавлены за границами файлов шаблона сайта. То есть подключили мы шаблон, начали выполнять /index.php — всё, начало действовать пространство страницы. И все компоненты, которые выполняются во время выполнения данного файла при добавлении нового ассетса в общий список маркируются пространством PAGE.
Вот почему например, если у нас какой-то компонент находился в шаблоне сайта (или до него дело доходило во время фактического выполнения header.php — например, через подключаемые области или шаблоны других компонентов), его ассетсы объединялись в кеш-файл template_*, а как только мы перенесли компонент на страницу, ассетсы переехали в page_*-файл.
Признак: кеш-файл, подключенный в итоговый html-кода страницы, имеет в пути вкрапление page_, пример:
/bitrix/cache/css/>/>/page_e4f06/page_e4f06_v1.css
¶2. Пользовательские потоки ассетсов
Мы можем в любой момент времени, находясь в любом из пространств, прервать его (или вклиниться) и начать формировать своё пространство.
- запоминаем текущий открытый поток (мы же не вредители, мы хотим грамотно вклиниться в процесс);
- начинаем формировать свой поток.
Важно! Прервать кастомный поток и дополнить ниже по коду мы не можем (в отличие от стандартных потоков); - набрасываем в поток ассетсы;
- возвращаем поток, который действовал до того, как мы вклинились.
use Bitrix\Main\Page\Asset; $asset = Asset::getInstance(); $assetToRestore = $asset->getTargetName(); // Использовать только ЗАГЛАВНЫЕ_НАЗВАНИЯ $asset->startTarget('ALEXEY_GFI'); // Устанавливаем идентификатор: $asset->setUnique('ALEXEY_GFI', 'gfi'); // . целевой файл будет выглядеть так: // /bitrix/cache/css/>/>/_gfi/_gfi_v1.css $asset->addCss('/css/style.css'); $asset->addJs('/local/js/script.js'); // Стоп-аем наш поток перед возвратом в стандартный // . иначе не сможем переключить $asset->stopTarget(); // Восстанавливаем поток, который действовал до нас $asset->startTarget($assetToRestore);
¶3. Накапливание ассетсов в кастомный поток
Накапливать один и тот же кастомный поток из разных мест мы не можем. То есть как только мы стоп-аем кастомный поток, его повторно уже запустить не получится. Если же нам нужно иметь возможность накапливать кастомный поток, рекомендую набросать свой хелпер (рассмотрено на видео).
Действия при работе с хелпером:
- накапливаем ассетсы в массив хелпера. Из любого места проекта, под любыми кодами потоков, с любым набором ассетсов
- когда приходит момент — как можно ближе к формированию html-кода, но перед компиляцией ассетсов ядром Битрикса — хелпер формирует на основе собранных данных кастомные потоки и наполняет их ассетсами.
Код класса-хелпера из видео:
namespace AlexeyGfi; use Bitrix\Main\Page\Asset; class AssetsMergeHelper < // Структура потоков protected static $assets = []; // Накапливаем css по разным потокам public static function add($streamName = null, $cssAsset = null) < if (empty($streamName) || empty($cssAsset)) < return; >if (!is_array($cssAsset)) < $cssAsset = [$cssAsset]; >$streamName = ToUpper($streamName); self::$assets[$streamName] = self::$assets[$streamName] ?? []; foreach ($cssAsset as $css) < self::$assets[$streamName][] = $css; >> // Выгружаем накопленные потоки // // Метод подписан на событие // main / onBeforeEndBufferContent public static function finalize() < if (empty(self::$assets)) < return; >$asset = Asset::getInstance(); $assetToRestore = $asset->getTargetName(); foreach(self::$assets as $targetName => $assetsList) < $asset->startTarget($targetName); $asset->setUnique($targetName, ToLower($targetName)); foreach($assetsList as $path) < $asset->addCss($path); > > $asset->stopTarget(); $asset->startTarget($assetToRestore); > >
Объединение JavaScript и CSS в одном файле
Если Вы работали над оптимизацией загрузки страницы, Вы знаете, насколько дорогостоящая каждая загрузка ресурса. Чем больше число внешних ресурсов, к которым Вы обращаетесь, тем больше время требуется для загрузки страницы.
Как правило, веб-страницы обращаются ко многим внешним CSS и файлам JS и следовательно подвергаются многим загрузкам ресурса. Совет от гуру оптимизации в необходимости объединить все файлы CSS и все до одного файлы JS, чтобы уменьшить число ресурсов до двух. Это, без сомнения, поможет сократить время загрузки страницы.
Если Вы все же считаете что эти две загрузки не являются лучшим решениям, я с Вами соглашусь. В этой статье мы рассмотрим способ объединения CSS с JS и сведем количество загрузок к одной. Я обнаружил этот способ, отчаянно пытаясь оптимизировать страницы в Microsoft Office Live
Техника основана на том как CSS и анализатор JS ведут себя в IE и Firefox.
• Когда анализатор CSS сталкивается с символом комментария HTML (