Карта с офисом — на странице контактов. Логично ожидать, что API карт грузится там же. На одном сайте мы нашли API Яндекс Карт на каждой странице с формой — то есть почти на всех. 465 КБ скриптов и 350 мс блокировки главного потока у каждого посетителя, хотя сама карта была только на странице контактов.
Это тридцать третья часть цикла «PageSpeed без мифов».
Как карты оказались везде
API карт подключался в сборке как «внешний модуль» — так удобно писать import ymaps from "ymaps" в коде. Казалось, что он загрузится только при import() там, где нужна карта. На деле внешний модуль типа script загружается, когда выполняется файл сборки, в котором он объявлен, — а этот файл грузился на всех страницах с формой. Мест, откуда в сборку попадали карты, по коду нашлось шесть.
Проверка: откройте страницу без карты, вкладка Network, фильтр по «maps». Если API грузится там, где карты нет, — та же история.
Лечение: свой загрузчик
Вместо внешнего модуля сборки — маленький загрузчик, который вставляет скрипт API, когда его действительно попросили, и отдаёт один и тот же промис всем, кто попросит повторно:
// ymaps.js — API карт загружается один раз и только по запросу
let api = null;
export function loadMaps() {
if (api) return api;
api = new Promise((resolve, reject) => {
const s = document.createElement("script");
s.src = "https://api-maps.yandex.ru/2.1/?apikey=КЛЮЧ&lang=ru_RU";
s.async = true;
s.onload = () => window.ymaps.ready(() => resolve(window.ymaps));
s.onerror = reject;
document.head.append(s);
});
return api;
}
// карта на странице: грузим, когда блок рядом с экраном, и не раньше, чем страница загрузилась
const box = document.querySelector("[data-map]");
if (box) {
const io = new IntersectionObserver(([e]) => {
if (!e.isIntersecting) return;
io.disconnect();
const go = () => loadMaps().then(ymaps => drawMap(ymaps, box));
document.readyState === "complete" ? go() : addEventListener("load", () => setTimeout(go, 1500), { once: true });
}, { rootMargin: "200px 0px" });
io.observe(box);
}
- Один промис — сколько бы компонентов ни запросили карты, скрипт загрузится один раз.
- По приближению к экрану — карта внизу страницы не грузится, пока до неё не долистали.
- Не раньше полной загрузки страницы и с запасом в полторы секунды — тяжёлый чужой скрипт не попадёт в окно первой отрисовки, даже если блок карты близко.
Отдельная ловушка: большой запас наблюдателя
Компоненты с большим rootMargin (например, 300 px у карты или 100% у видео) на телефоне оказываются «почти на первом экране»: наблюдатель срабатывает сразу после отрисовки, и тяжёлый сторонний код едет вместе с первым экраном. Для тяжёлого чужого кода берите небольшой запас и добавляйте условие «после load», как в примере.
Ещё дешевле: картинка вместо карты
Если карта нужна только чтобы показать, где офис, можно вообще не грузить API: показать статичную картинку карты (Static API у Яндекса и Google) со ссылкой «открыть в Картах». Интерактивную карту — по клику. Это ноль скриптов до действия посетителя.
Карты, чаты и виджеты грузятся там, где не нужны?
Пришлите адрес — найду лишние подключения и сколько они стоят.
// загружаю комментарии…