Битрикс. Композитный сайт. Часть 2 из 3

22.11.2018

Теги: CMSWeb-разработкаБитриксКешированиеКомпозитныйСайтНастройка

Как работает композитный сайт

Если страницы еще нет в кеше, система формирует страницу так, как если бы сайт работал без поддержки композита. И в таком виде отдает страницу браузеру. После чего подготавливает страницу к сохранению в кеше. По меткам в html-коде, расставленным программистом, вырезает из страницы динамические блоки и вставляет вместо них статические заглушки. Эти заглушки могут быть просто текстом «Загрузка...», анимированной картинкой loader.gif или каким-либо html-кодом. Кроме того, система добавляет к странице javascript-код, который: во-первых, выполнит фоновый ajax-запрос на получение динамического контента, а во-вторых, вставит полученные динамические блоки на место заглушек. Теперь страница содержит только статику и у нее есть ajax-загрузчик. В этом виде страница сохраняется в кеш.

Если страница есть в кеше, она будет отдана практически мгновенно, без выполнения php-кода и запросов к базе данных. Браузер, получив страницу из кеша, выполнит фоновый ajax-запрос, получит от сервера актуальные данные в формате json и вставит на место заглушек динамические блоки.

При фоновом ajax-запросе Битрикс формирует страницу полностью. Из полученного html-кода страницы вырезаются динамические данные и отправляются браузеру в формате json. На место вырезанных динамических блоков вставляются заглушки, на страницу добавляется ajax-загрузчик. После замены всей динамики на статику и добавления ajax-загрузчика, вычисляется контрольная сумма получившейся страницы. Если эта сумма не совпадает с контрольной суммой страницы в кеше — кеш перезаписывается.

Адаптация шаблона компонента

Перевод сайта на использование композита подразумевает настройку всех используемых компонентов и их шаблонов на корректную работу в композитном режиме. По умолчанию логика работы следующая: все компоненты «голосуют» за включение технологии, а шаблоны — «голосуют» против. Поведение по умолчанию можно изменить: шаблоны компонентов при ручной настройке композита должны быть определены как:

  1. Статичный шаблон, его можно записывать в кеш
    $this->setFrameMode(true);
  2. Динамичный шаблон, его нельзя записывать в кеш
    $this->setFrameMode(false);
  3. Статичный шаблон с динамичными зонами
    $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

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.