При обычном рендере страницы парсер запускает все модули последовательно: сначала роутер, затем шапка, меню, контент, блоки рекомендаций, виджеты — и только после того, как все они отработали, страница отдаётся браузеру. Если один из модулей выполняет тяжёлый запрос к базе данных или обращается к внешнему API, он задерживает отдачу всей страницы.
Отложенная загрузка (Lazy Loading) решает эту проблему. Идея проста: «тяжёлые» модули не запускаются вместе со страницей — вместо их результата парсер подставляет AJAX-заглушку. Браузер получает страницу мгновенно, отображает её пользователю, а затем в фоне запрашивает содержимое отложенных модулей и вставляет их результаты в соответствующие места без перезагрузки страницы.
Это особенно полезно для блоков, которые: - выполняют медленные агрегирующие SQL-запросы (статистика, рекомендации, «похожие товары»); - обращаются к внешним сервисам (курсы валют, наличие на складе через API поставщика); - персонализированы и не могут быть закешированы на уровне страницы; - не критичны для первичного восприятия страницы пользователем.
Отложенная загрузка — инструмент, которым легко злоупотребить. Если включить её для большинства модулей страницы, браузер отправит десятки параллельных AJAX-запросов сразу после загрузки — по одному на каждый модуль. Это создаст пиковую нагрузку на сервер, которая по характеру ничем не отличается от DDoS-атаки, только организованной самим сайтом.
Практическое правило: отложенная загрузка имеет смысл только для модулей, которые заметно медленнее остальных и не влияют на первичный контент страницы. Таких модулей на странице, как правило, один-два, редко больше трёх.
Для включения отложенной загрузки достаточно установить флаг «Отложенная загрузка» в правой панели IDE в параметрах нужного модуля.
После этого поведение модуля меняется: при рендере страницы вместо
его результата парсер автоматически подставляет блок JavaScript-кода,
который: 1. Создаёт на странице контейнер с уникальным идентификатором
(id = md5(имя_модуля + параметры)). 2. Немедленно после
загрузки страницы отправляет AJAX-запрос за содержимым модуля. 3.
Вставляет полученный HTML в контейнер и выполняет все
<script>-теги из ответа.
Браузер Сервер (index.php)
| |
|── GET /?topic_id=5 ────────────────>|
| |── Run(melbis_base_page)
| | ├─ melbis_base_header [обычный]
| | ├─ melbis_cataloge [обычный]
| | ├─ melbis_store_topic [обычный]
| | └─ melbis_store_random [lazy] → AJAX-заглушка
|<── HTML страницы (быстро) ──────────|
|
|── POST /lazy/ {mod: melbis_store_random, params: ...} ──>|
| |── Run(melbis_store_random)
|<── HTML блока рекомендаций ───────────────────────────────|
|
[вставить в контейнер]
AJAX-запрос уходит на тот же index.php через маршрут
lazy/, который должен быть прописан в
.htaccess:
RewriteRule ^lazy/$ index.php?lazy [L,QSA]В корневом скрипте index.php этот маршрут обрабатывается
отдельной веткой:
if ( isset($_GET['lazy']) )
{
$entry_point = $_POST['mod'];
$entry_param = $_POST['params'];
}Параметры модуля передаются в URL-encoded виде через POST — парсер формирует их автоматически из тех же параметров, с которыми модуль был вызван в шаблоне. Разработчику ничего дополнительно делать не нужно: включил галочку — модуль стал lazy, выключил — вернулся к обычному режиму.
Модуль с включённой отложенной загрузкой остаётся полноценной входной точкой — при lazy-вызове он обрабатывается точно так же, как при обычном, и может использовать кеш, библиотеки и любые другие возможности платформы.
Штатная заглушка — это готовый блок с XMLHttpRequest на
чистом JavaScript, без зависимостей от библиотек на странице. Обычно её
менять не нужно, но два метода позволяют заменить и адрес, и саму
заглушку. Вызываются они в корневом скрипте, до Run().
DefineLazyScript($mUrl) — адрес, на
который уходит AJAX-запрос. По умолчанию lazy/ от корня
сайта. Меняется, если маршрут в .htaccess назван иначе или
запросы отложенной загрузки нужно увести на отдельный домен:
MELBIS()->DefineLazyScript('/ajax/lazy/');DefineLazyLoader($mHtml) — сам HTML-код
заглушки. В переданном тексте парсер подставит четыре ключа:
| Ключ | Значение |
|---|---|
{SCRIPT} |
адрес из DefineLazyScript |
{MODULE} |
имя отложенного модуля |
{PARAMS} |
параметры вызова, в URL-кодированном виде |
{ID} |
уникальный идентификатор контейнера |
$loader = '<div id="{ID}" class="lazy-block"><span class="spinner"></span></div>
<script>
fetch("{SCRIPT}", {
method: "POST",
headers: {"Content-type": "application/x-www-form-urlencoded"},
body: "mod={MODULE}¶ms={PARAMS}"
})
.then(function(r) { return r.text(); })
.then(function(html) { document.getElementById("{ID}").innerHTML = html; });
</script>';
MELBIS()->DefineLazyLoader($loader);Заменяют заглушку обычно ради двух вещей: показать скелетон или
спиннер вместо пустого места, пока блок грузится, и выполнить
<script>-теги из ответа — штатный загрузчик это
делает, и своя реализация на innerHTML не
сделает. Если отложенный модуль возвращает разметку со
скриптами, их запуск придётся предусмотреть в своём загрузчике
самостоятельно.