Блоки ниже сгиба — отзывы, подборки товаров, карты, слайдеры — часто грузятся отдельным запросом, когда до них доходит прокрутка. Идея правильная: первый экран рисуется быстро, остальное приходит по мере надобности. Но у такой схемы почти всегда два пути загрузки, и если отложить только один, второй тихо тащит всё в окно первой отрисовки.
Это десятая часть цикла «PageSpeed без мифов».
Два пути загрузки
- По приближению к экрану.
IntersectionObserverс запасом (rootMargin) видит, что блок скоро появится, и запускает загрузку. - Фоновая догрузка «на всякий случай». Чтобы быстрый посетитель, резко пролиставший вниз, не упёрся в пустоту, оставшиеся блоки после загрузки страницы догружаются сами, по очереди.
Второй путь часто дописывают позже первого, отдельной функцией, и про него забывают, когда ставят «ворота после отрисовки» из восьмой части.
Как это нас подвело
Ворота мы поставили на наблюдатель — и на тестовом сервере всё выглядело идеально: блоки грузились после отрисовки. На рабочем сайте оценка не выросла. Разбор окна показал причину: фоновая догрузка стартовала по своему таймеру и обгоняла отрисовку на 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), иначе отложенная загрузка превратится в сдвиги вёрстки.
Может, проще отказаться от фоновой догрузки?
Тогда содержимое блоков не увидит поисковый робот: он не прокручивает страницу, и наблюдатель для него не сработает никогда. Подробно — в одиннадцатой части.
Ленивые блоки, а оценка всё равно низкая?
Пришлите адрес — проверю оба пути загрузки и окно первой отрисовки.
// загружаю комментарии…