Общее описание
Итак, у нас есть страница, которая по каким-то причинам долго формирует часть данных. Причины могут быть разными — большой объём вычислений, большой объём данных, долгие запросы к какой-нибудь медленной базе данных, или вообще надо лезть на сторонний сервис, с которого получить информацию, которую потом ещё и обработать надо. Причины — неважны. Важно, что на нашей странице есть данные, которые мы может отдать сразу — заголовки, подвал, какие-то картинки, ещё кучу обрамления, текста, реклама, в конце концов. А есть данные, которые нам надо «добыть», и на это уйдёт какое-то количество времени.
Так вот. Обычно эту задачу решают через механизм «ленивой загрузки» — грузится облегчённая версия страницы, а потом срабатывает скрипт 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.
Спасибо за внимание.
ссылка на оригинал статьи https://habr.com/ru/post/534792/
Добавить комментарий