Битрикс. Композитный сайт. Часть 2 из 3
22.11.2018
Теги: CMS • Web-разработка • Битрикс • Кеширование • КомпозитныйСайт • Настройка
Как работает композитный сайт
Если страницы еще нет в кеше, система формирует страницу так, как если бы сайт работал без поддержки композита. И в таком виде отдает страницу браузеру. После чего подготавливает страницу к сохранению в кеше. По меткам в html-коде, расставленным программистом, вырезает из страницы динамические блоки и вставляет вместо них статические заглушки. Эти заглушки могут быть просто текстом «Загрузка...», анимированной картинкой loader.gif
или каким-либо html-кодом. Кроме того, система добавляет к странице javascript-код, который: во-первых, выполнит фоновый ajax-запрос на получение динамического контента, а во-вторых, вставит полученные динамические блоки на место заглушек. Теперь страница содержит только статику и у нее есть ajax-загрузчик. В этом виде страница сохраняется в кеш.
Если страница есть в кеше, она будет отдана практически мгновенно, без выполнения php-кода и запросов к базе данных. Браузер, получив страницу из кеша, выполнит фоновый ajax-запрос, получит от сервера актуальные данные в формате json и вставит на место заглушек динамические блоки.
При фоновом ajax-запросе Битрикс формирует страницу полностью. Из полученного html-кода страницы вырезаются динамические данные и отправляются браузеру в формате json. На место вырезанных динамических блоков вставляются заглушки, на страницу добавляется ajax-загрузчик. После замены всей динамики на статику и добавления ajax-загрузчика, вычисляется контрольная сумма получившейся страницы. Если эта сумма не совпадает с контрольной суммой страницы в кеше — кеш перезаписывается.
Адаптация шаблона компонента
Перевод сайта на использование композита подразумевает настройку всех используемых компонентов и их шаблонов на корректную работу в композитном режиме. По умолчанию логика работы следующая: все компоненты «голосуют» за включение технологии, а шаблоны — «голосуют» против. Поведение по умолчанию можно изменить: шаблоны компонентов при ручной настройке композита должны быть определены как:
-
Статичный шаблон, его можно записывать в кеш
$this->setFrameMode(true);
-
Динамичный шаблон, его нельзя записывать в кеш
$this->setFrameMode(false);
- Статичный шаблон с динамичными зонами
$this->createFrame()->begin('Загрузка...');
Со статичным и динамичным шаблоном все понятно, а вот статичный шаблон с динамичными зонами рассмотрим подробнее. Самый простой вариант — когда весь шаблон компонента включается в динамическую зону:
<?php if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die(); // весь шаблон компонента включается в динамическую зону $this->createFrame()->begin('Загрузка...'); ?> <h1><?= $arResult['NAME']; ?></h1> <article id="iblock-element"> <img src="<?= $arResult['DETAIL_PICTURE']['SRC']; ?>" alt="<?= $arResult['DETAIL_PICTURE']['ALT']; ?>" title="<?= $arResult['DETAIL_PICTURE']['TITLE']; ?>" /> <p>Количество просмотров: <?= $arResult['SHOW_COUNTER'] ? $arResult['SHOW_COUNTER'] : 0; ?></p> <div> <?= $arResult['DETAIL_TEXT'] ?> </div> <p><a href="<?= $arResult['SECTION']['SECTION_PAGE_URL']; ?>">Назад в раздел</a></p> </article>
Если код $this->createFrame()->begin('Загрузка...');
стоит в начале компонента и нигде не завершается (зона в этом случае завершается автоматически), то реально это означает, что весь компонент — динамичный. В кеш будет записана заглушка, пользователь увидит на мгновение текст «Загрузка...», а после ajax-запроса — на место заглушки будет вставлен html-код шаблона компонента.
<div id="bxdynamic_vpV9RD_start" style="display:none"></div> Загрузка... <div id="bxdynamic_vpV9RD_end" style="display:none"></div>
<div id="bxdynamic_vpV9RD_start" style="display:none"></div> <h1>Абиссинская кошка</h1> <article id="iblock-element"> <img src="/upload/iblock/b59/b598753f287a05c04ca3079764033bca.jpg" alt="Абиссинская кошка. Породы кошек. Статьи о домашних животных" title="Абиссинская кошка. Породы кошек. Статьи о домашних животных" /> <p>Количество просмотров: 5</p> <div> <p> Абиссинская кошка — одна из самых древних пород кошек. Изображение кошек на древних египетских росписях, а также указание их цвета в описаниях «солнечный кот», «лазуритовая кошка горизонта» наводят на мысли о том, что речь шла именно об абиссинских кошках. </p> <p> .......... </p> </div> <p><a href="/test/category/id/16/">Назад в раздел</a></p> </article> <div id="bxdynamic_vpV9RD_end" style="display:none"></div>
Если вместо текста «Загрузка...» нужна пустая заглушка, то методу begin()
надо передать пустую строку:
$frame = $this->createFrame()->begin(''); // динамический контент $frame->end();
Если необходимо, чтобы в заглушке отображались данные компонента, полученные на предыдущем хите, то метод begin()
необходимо вызвать без параметров:
$frame = $this->createFrame()->begin(); // динамический контент = заглушка $frame->end();
В качестве параметра методу begin()
можно передать анимированное изображение:
$this->createFrame()->begin('<img src="/images/loader.gif" alt="" />');
Еще один вариант формирования заглушки и динамического контента:
$frame = $this->createFrame()->begin(); // динамический контент $frame->beginStub(); // html-код заглушки $frame->end();
В разметке сначала указывается собственно динамичная часть, которая будет различной для каждого пользователя, а затем, через вызов beginStub()
— та часть, которая будет записана в кеш и которая будет показана пользователю, пока не получены данные из первой части.
Код между вызовами begin()…beginStub()
и beginStub()…end()
выполняется всегда. И на первом хите к странице (на котором создается кеш) и на ajax-хите. Эти методы занимаются буферизацией контента и не являются аналогами конструкции if…else
. Вызов $frame->end()
завершает разметку динамичной части в шаблоне компонента.
$this->StartResultCache()
) и при этом шаблон голосовал «против», то композитный режим будет отменен.
Динамичных зон в шаблоне компонента может быть несколько, но они не могут быть вложенными друг в друга:
<div id="iblock-section-items"> <section> <?php foreach ($arResult['ITEMS'] as $i => $arItem): ?> <article> <?php $frame = $this->createFrame()->begin('Загрузка элемента ' . $i . '...'); ?> <h3><?= $arItem['NAME']; ?></h3> <p><?= $arItem['PREVIEW_TEXT']; ?></p> <span>Количество просмотров: <?= $arItem['SHOW_COUNTER'] ? $arItem['SHOW_COUNTER'] : 0; ?></span> <?php $frame->end(); ?> </article> <?php endforeach; ?> </section> </div>
<div id="iblock-section-items"> <section> <article> <div id="bxdynamic_30bHoO_start" style="display:none"></div> Загрузка элемента 0... <div id="bxdynamic_30bHoO_end" style="display:none"></div> </article> <article> <div id="bxdynamic_VTD1tH_start" style="display:none"></div> Загрузка элемента 1... <div id="bxdynamic_VTD1tH_end" style="display:none"></div> </article> <article> <div id="bxdynamic_tzA6ax_start" style="display:none"></div> Загрузка элемента 2... <div id="bxdynamic_tzA6ax_end" style="display:none"></div> </article> </section> </div>
<div id="iblock-section-items"> <section> <article> <div id="bxdynamic_30bHoO_start" style="display:none"></div> <h3>Английский бульдог</h3> <p>По названию («bull» переводится «бык», «dog» — собака) понятно, что бульдог был предназначен...</p> <span>Количество просмотров: 5</span> <div id="bxdynamic_30bHoO_end" style="display:none"></div> </article> <article> <div id="bxdynamic_VTD1tH_start" style="display:none"></div> <h3>Далматин</h3> <p>О точном происхождении далматинов известно мало. Считается, что их родиной была Далмация...</p> <span>Количество просмотров: 3</span> <div id="bxdynamic_VTD1tH_end" style="display:none"></div> </article> <article> <div id="bxdynamic_tzA6ax_start" style="display:none"></div> <h3>Афганская борзая</h3> <p>Изящная красавица с длинной развевающейся на бегу шелковистой шерстью, афганская борзая...</p> <span>Количество просмотров: 1</span> <div id="bxdynamic_tzA6ax_end" style="display:none"></div> </article> </section> </div>
Вставка динамического контента после ajax-запроса создает неприятный эффект — элементы страницы резко смещаются вниз, освобождая место под динамический контент. Давайте создадим свой контейнер с уникальным идентификатором для динамического контента и стилизуем его — установим min-height
, чтобы место под динамический контент было зарезервировано сразу. Кроме того, используем метод setAnimation()
, который плавно изменяет opacity
контейнера от нуля до единицы:
<div id="iblock-section-items"> <section> <?php foreach ($arResult['ITEMS'] as $i => $arItem): ?> <article id="iblock-element-<?= $i; ?>"> <!-- идентифкатор динамического контента --> <?php $frame = $this->createFrame()->begin('Загрузка элемента ' . $i . '...'); // задаем свой идентификатор динамического контента, вместо сгенерированного системой $frame->setContainerID('iblock-element-'.$i); // добавляем анимацию динамического контента после ajax-запроса; opacity плавно изменяется от 0 до 1 $frame->setAnimation(true); ?> <img src="<?= $arItem['PREVIEW_PICTURE']['SRC']; ?>" alt="" /> <h3><?= $arItem['NAME']; ?></h3> <p><?= $arItem['PREVIEW_TEXT']; ?></p> <span>Количество просмотров: <?= $arItem['SHOW_COUNTER'] ? $arItem['SHOW_COUNTER'] : 0; ?></span> <?php $frame->end(); ?> </article> <?php endforeach; ?> </section> </div>
/* задаем min-height для трех контейнеров */
#iblock-element-0, #iblock-element-1, #iblock-element-2 {
min-height: 128px;
}
<div id="iblock-section-items"> <section> <article id="iblock-element-0"> Загрузка элемента 0... </article> <article id="iblock-element-1"> Загрузка элемента 1... </article> <article id="iblock-element-2"> Загрузка элемента 2... </article> </section> </div>
<div id="iblock-section-items"> <section> <article id="iblock-element-0"> <img src="/upload/iblock/c41/c4109603b6e02c31d681583909af6205.jpg" alt="" /> <h3>Английский бульдог</h3> <p>По названию («bull» переводится «бык», «dog» — собака) понятно, что бульдог был предназначен...</p> <span>Количество просмотров: 2</span> </article> <article id="iblock-element-1"> <img src="/upload/iblock/c96/c9661e9c9cae4c7bca190da9289b8fbc.jpg" alt="" /> <h3>Далматин</h3> <p>О точном происхождении далматинов известно мало. Считается, что их родиной была Далмация...</p> <span>Количество просмотров: 0</span> </article> <article id="iblock-element-2"> <img src="/upload/iblock/32d/32de49a7c07885d78d42a21ac7f16ed2.jpg" alt="" /> <h3>Афганская борзая</a></h3> <p>Изящная красавица с длинной развевающейся на бегу шелковистой шерстью, афганская борзая...</p> <span>Количество просмотров: 0</span> </article> </section> </div>
Использование Web SQL браузера
Можно использовать локальную базу данных браузера, чтобы сохранять в ней динамические блоки страниц. Когда браузер получает страницу из кеша, он выполняет фоновый ajax-запрос на получение динамичных блоков, чтобы заменить заглушки. Получение актуальных данных с сервера может занять какое-то время. Если последние актуальные данные с сервера сохранять в локальной базе данных браузера, мы можем показать их практически сразу.
Давайте посмотрим, как это работает на примере шаблона компонента, который выводит детальную информацию об элементе инфоблока:
<?php if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die(); // Обратите внимание, что для метки динамического контента задается уникальный идентификатор $frame = $this->createFrame('iblock-element-'.$arResult['ID'])->begin('Загрузка...'); $frame->setBrowserStorage(true); ?> <h1><?= $arResult['NAME']; ?></h1> <article id="iblock-element"> <img src="<?= $arResult['DETAIL_PICTURE']['SRC']; ?>" alt="<?= $arResult['DETAIL_PICTURE']['ALT']; ?>" title="<?= $arResult['DETAIL_PICTURE']['TITLE']; ?>" /> <p>Количество просмотров: <?= $arResult['SHOW_COUNTER'] ? $arResult['SHOW_COUNTER'] : 0; ?></p> <div> <?= $arResult['DETAIL_TEXT']; ?> </div> <p><a href="<?= $arResult['SECTION']['SECTION_PAGE_URL']; ?>">Назад в раздел</a></p> </article> <?php $frame->end(); ?>
Обратите внимание, что для метки динамического контента задается уникальный идентификатор. Если бы код был таким:
$frame = $this->createFrame()->begin('Загрузка...'); $frame->setBrowserStorage(true); /*...*/ $frame->end();
<div id="bxdynamic_vpV9RD_start" style="display:none"></div> Загрузка... <div id="bxdynamic_vpV9RD_end" style="display:none"></div>
База данных браузера содержала бы всего одну запись с идентификатором bxdynamic_vpV9RD
, которая бы постоянно перезаписывалась. И на странице элемента инфоблока «Абиссинская кошка» мы бы видели данные другого элемента, например «Афганская борзая». Правда, очень короткий интервал времени — между моментом вставки динамических блоков из локальной базы браузера и до момента вставки динамических блоков, полученных от сервера.
Этот пример показывает, как можно сохранить в локальную SQL-базу динамический контент, но делать так не следует. Потому что здесь в локальную базу сохраняются целые статьи. Пример правильного использования — корзина интернет-магазина, которая показывается на всех страницах сайта. Это совсем маленький динамический блок и у него всегда один идентификатор, например bxdynamic_VTD1tH
. Запись в локальной базе для этого блока будет только одна и в ней — последние актуальные данные корзины.
Динамические зоны в шаблоне сайта
Как уже упоминалось выше, чтобы страница сайта попала в кеш, за композит должны проголосовать все компоненты, в том числе и те, которые подключаются в шаблонах сайта. Давайте на какой-нибудь странице подключим компонент, который выводит на странице несколько случайных элементов инфоблока. Такой компонент всегда голосует против композита, иначе мы не получим случайные элементы:
<?php /* * Файл local/components/tokmakov/iblock.element/component.php */ if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die(); // Компонент голосует против композита, его нельзя кешировать, // иначе мы не получим случайные элементы инфоблока $this->setFrameMode(false);
<?php /* * Файл local/components/tokmakov/iblock.random/templates/.default/template.php */ if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die(); // Шаблон компонента голосует против композита, его нельзя кешировать, // иначе мы не получим случайные элементы инфоблока $this->setFrameMode(false);
Но если подключение такого компонента происходит в шаблоне сайта, то все страницы, использующие этот шаблон, не смогут работать в композитном режиме.
<?php require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php"); $APPLICATION->SetTitle("Случайные элементы"); ?> <h1>Случайные элементы</h1> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore... </p> <?php // подключаем компонент, который формирует блок случайных элементов $APPLICATION->IncludeComponent( "tokmakov:iblock.random", "", array( "ELEMENT_COUNT" => "4", "ELEMENT_URL" => "#SITE_DIR#/#IBLOCK_CODE#/element/code/#ELEMENT_CODE#/", "IBLOCK_ID" => "5", "IBLOCK_TYPE" => "content" ) ); ?> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore... </p> <?php require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php"); ?>
#WORK_AREA#
, но подключение такого компонента может потребоваться и в файлах шаблонов сайта header.php
и footer.php
.
Чтобы все-таки заставить такие страницы работать в композитном режиме, предназначены классы FrameStatic
и FrameBuffered
. Так же, как и в шаблоне компонента, мы расставляем отметки в html-коде, где будет начало и конец динамического контента. Или, сообщаем системе идентификатор контейнера с динамичеким контентом.
- Класс
FrameStatic
просто расставляет метки начала и конца динамической зоны. И потом, когда страница сформирована, по этим меткам вырезается контент динамической области. - Класс
FrameBuffered
мы уже использовали, когда адаптировали шаблон компонента для работы в композитном режиме. Именно объект этого класса создаётся при вызове метода$this->createFrame()
в шаблоне компонента.
Поиск: CMS • Web-разработка • Битрикс • Кеширование • Композитный сайт • Настройка • setFrameMode • createFrame • begin • beginStub • end • setContainerID • setAnimation