#lazy_builder (не путать с lazy load) в Drupal 8/9

    Продолжая изучать Drupal, мы наткнулись на очень интересную технологию, которая в него встроена. И которая, на наш взгляд, используется неоправданно мало. И совершенно зря. Потому что подход, который использует эта технология, позволяет мгновенно отдать высоконагруженную (или просто долго формирующуюся) страницу пользователю, а потом «доотдать» ему данные, требующие много времени на формирование. И изучая эту технологию, мы столкнулись с тем, что нет ни одного простого объяснения, что это и как это запустить. Нет, объяснения-то есть. Простых нету. Сейчас мы попытаемся восполнить эту неприятность.

    Общее описание


    Итак, у нас есть страница, которая по каким-то причинам долго формирует часть данных. Причины могут быть разными — большой объём вычислений, большой объём данных, долгие запросы к какой-нибудь медленной базе данных, или вообще надо лезть на сторонний сервис, с которого получить информацию, которую потом ещё и обработать надо. Причины — неважны. Важно, что на нашей странице есть данные, которые мы может отдать сразу — заголовки, подвал, какие-то картинки, ещё кучу обрамления, текста, реклама, в конце концов. А есть данные, которые нам надо «добыть», и на это уйдёт какое-то количество времени.

    Так вот. Обычно эту задачу решают через механизм «ленивой загрузки» — грузится облегчённая версия страницы, а потом срабатывает скрипт lazy loading, который с помощью JavaScript сходит на бекенд, заберёт оттуда данные и положит их в нужные места. Но у ленивой загрузки есть куча минусов. Начиная с того, что если, например, наши данные отдаются только авторизованному пользователю, то нам надо дополнительно авторизовать этот ленивый скрипт. Также нам всегда нужен включённый JavaScript. Эта «ленивая вещь» не очень хорошо дружит в поисковыми роботами. Ну и так далее.

    А создатели Drupal, оказывается, молодцы. И предлагают нам ещё один механизм, который лишён почти всех минусов lazy loading'а. И называется этот механизм — «ленивый построитель» — lazy builder. Он работает, как всё гениальное — очень просто.
    На то место в twig-шаблоне, где нужно вывести «тяжелые» данные, мы кладём обычную (почти) переменную, совершенно обычным способом, вот так: {{ lazy_data }}. А вот в препроцессоре, в котором мы готовим эту переменную, мы должны сказать ей волшебные слова, чтобы она стала lazy builder'ом. Выглядит это так:

    $variables['lazy_data'] = [
      '#create_placeholder' => TRUE, // - это необязательно, её Drupal сам умеет включать.
      '#lazy_builder' => [ // - это те самые волшебные слова, из-за которых всё работает.
        //... чуть ниже будет подробнее
        ],
      ],
    ];

    И теперь Drupal, когда будет рендерить страницу, на место этой переменной поставит JavaScript-плейсхолдер, а сами данные формировать в момент рендера не будет. То есть — эта страница сформируется быстро, и так же быстро будет отдана пользователю. А уже потом, когда она будет показана браузером на экране, сработает этот плейсхолдер, который полезет на бэкэнд и скажет ему — «я готов, гони данные». Бэкэнд эти данные спокойно сформирует и отдаст. И они будут вставлены на то место, где они и должны быть.

    Вот и всё! И не надо никакой дополнительной авторизации — она уже была произведена на бэкэнде. И не надо подключать никаких дополнительных скриптов — Drupal сам обо всём позаботится. И не нужны никакие дополнительные API точки входа, которые надо дополнительно писать и обслуживать. Не надо никаких уточнений — что за данные вам нужны — всё это уже было сделано на бэкэнде. Мы просто кусок «строительства страницы» отложили на потом. Вот и всё, что мы сделали.

    И самое гениальное здесь то, что если у пользователя не включен JavaScript — то Drupal сам это распознает и сформирует эти тяжёлые данные сразу, без использования плейсхолдера. То есть — как будто бы никакой механиз отложенного строительства и не существует. Поисковые роботы будут счастливы, что им не надо разбираться с самописными ленивыми загрузками, они получат обычную страницу. Ну а подождать несколько секунд эту страницу для них не так критично, как для человека.

    А теперь немного технических подробностей, как со всем этим взлететь это сделать


    Повторимся — в twig-шаблоне мы пишем как обычно — просто кладём переменную в нужное нам место. И всё, больше никаких телодвижений предпринимать не нужно.
    В препроцессоре шаблона, в котором нам надо сформировать эту переменную, мы пишем «волшебные слова», чтобы она стала «ленивой» и пишем, что она должна вызвать для ленивого рендера нашей переменной:

    $variables['lazy_data'] = [
      '#create_placeholder' => TRUE, // - это необязательно, её Drupal сам умеет включать.
      '#lazy_builder' => [ // - это волшебные слова, из-за которых всё работает.
        'имя_модуля.lazy_renderer:renderBigData', [ // - это имя сервиса, который будет вызываться. Важно, что это именно сервис.
          'info' => 'Важная информация', // - это просто параметры, которые ты отдаёшь в шаблон. См. ниже
          'params' => ['foo' => 'boo'],
            'something_else' => 11
        ],
      ],
    ];

    Теперь нам надо сделать Drupal-овский «сервис», который и будет заниматься формированием наших больших данных. Для этого в файле имя_модуля.service.yml, который лежит в корне нужного модуля (не обязательно самописного), мы должны объявить этот сервис (имя_модуля.lazy_renderer), который будет вызываться для формирования того, что надо вывести в переменную 'lazy_data', которая потом пойдёт в ту заглушку, в дом который построил Джек.

    В этом сервисе делаем функцию renderBigData, которая и будет вызываться. И вот эта функция должна вернуть ссылку на шаблон, который будет отрендерен и будет вставлен в нужное место страницы, в доме который построил Джек.

    Но в эту функцию хочется же передать что-нибудь, да? Вот как это делается.

    Во-первых, напоминаем, что для того, чтобы продать что-нибудь ненужное, надо сначала купить что-нибудь ненужное, а для того чтобы использовать шаблон для рендера, его надо сначала объявить. То есть — в файле имя_модуля.module, в функции function promotion_theme(...) надо вернуть, вместе с остальными заготовками шаблонов, заготовку нового шаблона:

    'my_template_for_lazy_building' => [
      'variables' => [
        'info' => '',
        'params' => [],
        'something_else' => 1
      ],
    ],

    А дальше всё просто — в сервисе, в функции, подготавливается всё, что нужно, и этот свежеобъявленный шаблон возвращается на рендер, передав ему то, что тебе нужно ему передать.

    А у этого шаблона, в свою очередь, есть свой препроцессор, который сработает при попытке Drupal отрендерить шаблон. В этот момент препроцессор получит те переменные, которые объявлены и переданы.

    То есть, технически это выглядит так:

    // Смотрим выше на вызов сервиса из нашей переменной:
    // 'имя_модуля.lazy_renderer:renderBigData'
    // и сопоставляем класс:
    
    class lazy_renderer {
            public function renderBigData($info, $params, $something_else) {
            //Обрати внимание на переменные функции
        //Что-то тут делаем, готовим данные.
        return [
            '#theme' => 'my_template_for_lazy_building',
          '#info' => $info,
          '#params' => $params,
          '#something_else' => $something_else
        ];
      }
    }

    Сам twig-шаблон должен лежать в каталоге template модуля, само собой.

    Вот и всё.

    И ещё раз, чтобы подытожить «кто на ком стоял»:

    • У нас есть переменная «lazy_data». Мы её кладём в twig-шаблон какой-то страницы, как простую переменную.
    • В препроцессоре мы её формируем. И говорим ей — что она «ленивая» и что она должна вызвать сервис ('имя_модуля.lazy_renderer:renderBigData'), который вернёт шаблон (другой, 'my_template_for_lazy_building') на рендер. Этот шаблон отрендерится и вставится на место 'lazy data'.
    • Не забываем наш шаблон объявить.

    Надеемся, что смогли просто, но при этом максимально подробно, рассказать про технологию Lazy Builder.

    Спасибо за внимание.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 3

      +1
      #lazy_builder не имеет отношения к «ленивой загрузке» и тем более к javascript, этим всем занимается модуль BigPipe о котором почему-то ничего не сказано. #lazy_builder создан для возможности кэшировать страницы (и другой html) с динамическим содержимым.
        0
        Дополню, что BigPipe появился еще во времена Drupal7.
          0
          Согласны, спасибо за уточнение! #lazy_builder — это один из способов организации кэширования и оптимизации сайта, доступный разработчику. А BigPipe работает в ядре (самостоятельно, но и влияет на обработку #lazy_builder).

          Статья задумывалась как гайд начального уровня для разработчиков, первый раз столкнувшихся с необходимостью применения #lazy_builder, и мы не включали подробностей о том, что «под капотом».

        Only users with full accounts can post comments. Log in, please.