Блоки ниже сгиба — отзывы, подборки товаров, карты, слайдеры — часто грузятся отдельным запросом, когда до них доходит прокрутка. Идея правильная: первый экран рисуется быстро, остальное приходит по мере надобности. Но у такой схемы почти всегда два пути загрузки, и если отложить только один, второй тихо тащит всё в окно первой отрисовки.

Это десятая часть цикла «PageSpeed без мифов».

Два пути загрузки

  1. По приближению к экрану. IntersectionObserver с запасом (rootMargin) видит, что блок скоро появится, и запускает загрузку.
  2. Фоновая догрузка «на всякий случай». Чтобы быстрый посетитель, резко пролиставший вниз, не упёрся в пустоту, оставшиеся блоки после загрузки страницы догружаются сами, по очереди.

Второй путь часто дописывают позже первого, отдельной функцией, и про него забывают, когда ставят «ворота после отрисовки» из восьмой части.

Как это нас подвело

Ворота мы поставили на наблюдатель — и на тестовом сервере всё выглядело идеально: блоки грузились после отрисовки. На рабочем сайте оценка не выросла. Разбор окна показал причину: фоновая догрузка стартовала по своему таймеру и обгоняла отрисовку на 600 мс. В окно первой отрисовки уезжали разметка всех блоков и все их картинки — 1,2 МБ.

На тесте этого не было видно, потому что там страница рисовалась быстрее, чем срабатывал таймер догрузки. На боевом сайте, где отрисовка медленнее, порядок событий менялся.

Лечение: оба пути — через одни ворота

import afterFirstPaint from "./afterFirstPaint";

afterFirstPaint().then(() => {
  // путь 1: блок близко к экрану — грузим сразу
  const io = new IntersectionObserver(entries => entries.forEach(e => {
    if (e.isIntersecting) { io.unobserve(e.target); loadBlock(e.target); }
  }), { rootMargin: "1000px 0px" });
  const blocks = [...document.querySelectorAll("[data-lazy-block]")];
  blocks.forEach(el => io.observe(el));

  // путь 2: фоновая догрузка остальных — тоже только после ворот, по два за раз
  warmRemaining(blocks, 2);
});

Фоновую догрузку не убираем — она нужна и людям, и поисковикам (об этом следующая часть). Просто стартует она не по таймеру от начала загрузки, а после ворот.

Как найти второй путь у себя

  • Ищите в коде всё, что вызывает загрузку блоков. Не только наблюдатель, но и функции с названиями вроде preload, warm, loadRemaining, вызовы по load, setTimeout и requestIdleCallback.
  • Проверяйте по сети, а не по коду. В разборе окна (третья часть) ищите запросы блоков (XHR/Fetch) и их картинки. Если хоть один ушёл до отрисовки — путь не закрыт.
  • Проверяйте на боевом сайте или на копии с теми же данными: порядок событий зависит от скорости отрисовки.

Место под блоки резервируйте заранее (min-height или aspect-ratio), иначе отложенная загрузка превратится в сдвиги вёрстки.

Может, проще отказаться от фоновой догрузки?

Тогда содержимое блоков не увидит поисковый робот: он не прокручивает страницу, и наблюдатель для него не сработает никогда. Подробно — в одиннадцатой части.

Ленивые блоки, а оценка всё равно низкая?

Пришлите адрес — проверю оба пути загрузки и окно первой отрисовки.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.