Счётчик аналитики, карта кликов, виджет обратного звонка, онлайн-чат — каждый ставится «одной строчкой кода», и каждый тянет за собой десятки и сотни килобайт чужого JavaScript. Всё это выполняется в главном потоке браузера, и PageSpeed считает каждую миллисекунду. На одном сайте мы заблокировали сторонние скрипты для эксперимента — и оценка мобильной главной поднялась с 67 до 90, не тронув ни строчки собственного кода.

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

Где сторонние скрипты бьют по оценке

В отличие от шрифтов и картинок, главная беда здесь не вес в сети, а процессор. Модель PageSpeed замедляет его в четыре раза, а сторонний код обычно большой, разбирается и выполняется целиком. Это попадает в метрику TBT — суммарное время, на которое главный поток был занят длинными задачами.

Что было на главнойЗначение
виджет обратного звонка641 КБ, сервер виджета отдавал его без сжатия
+ счётчик аналитики с картой кликов
TBT в отчёте2,6–3,2 с
собственный код сайтаодна задача на 91 мс
оценка со сторонними скриптами / без них67 / 90
мобильная главная: почти вся блокировка — чужой код

Сделать чужой код быстрее мы не можем. Можем решить, когда он запускается.

Решение: загрузка после первого действия

Сторонние скрипты не нужны для того, чтобы человек увидел страницу. Аналитике достаточно начать работу, когда посетитель проявил себя: прокрутил, коснулся экрана, сдвинул мышь. Виджету звонка — тем более. Поэтому код сторонних сервисов кладём в <template> — браузер его не выполняет — и вставляем на страницу по первому действию посетителя или, если действий нет, через 3,5 секунды после полной загрузки.

<!-- в подвале: код счётчиков и виджетов — как есть, но внутри template -->
<template id="thirdParty">
  <script>/* код счётчика аналитики */</script>
  <script src="https://widget.example/callback.js" async></script>
</template>

<script>
(() => {
  // очередь для вызовов аналитики до её загрузки (формат официального кода Метрики)
  window.ym = window.ym || function () { (ym.a = ym.a || []).push(arguments); };

  let started = false;
  const run = () => {
    if (started) return;
    started = true;
    const tpl = document.getElementById("thirdParty");
    tpl.content.querySelectorAll("script").forEach(old => {
      const s = document.createElement("script");    // скрипт из template сам не выполнится
      [...old.attributes].forEach(a => s.setAttribute(a.name, a.value));
      s.text = old.text;
      document.body.append(s);
    });
  };
  ["pointerdown", "touchstart", "keydown", "scroll", "mousemove"].forEach(e =>
    addEventListener(e, run, { once: true, passive: true }));
  addEventListener("load", () => setTimeout(run, 3500));
})();
</script>

Три детали, без которых это не работает:

  1. Скрипт из template сам не выполнится. Если просто перенести содержимое шаблона в страницу, теги <script> окажутся мёртвыми. Их нужно пересоздать через document.createElement("script"), перенеся атрибуты и текст, — как в коде выше.
  2. Очередь для вызовов до загрузки. Формы и кнопки могут отправлять цели в аналитику раньше, чем она загрузилась. Заглушка window.ym складывает вызовы в очередь в том же формате, что и официальный код Метрики, — после загрузки счётчик их подберёт. Для других сервисов — аналогичная заглушка их формата (например, dataLayer для Google).
  3. Таймер после load, а не после загрузки DOM. 3,5 секунды отсчитываются от полной загрузки страницы: к этому моменту первый экран точно нарисован, и скрипты не попадут в окно первой отрисовки.

Проверьте, что механизм вообще включён. На том самом сайте отложенная загрузка была написана давно и работала… бы. Но шаблон подвала печатал коды счётчиков живыми тегами, а не внутри <template>, — и скрипты грузились сразу, мимо всего механизма. Проверка одной строкой: в исходном коде страницы коды счётчиков должны стоять внутри <template>.

Результат

Мобильная главная того сайта — с 67 до 99. Честности ради: 99 — итог нескольких правок, кроме сторонних скриптов там были ещё шрифты и сдвиги вёрстки из-за картинок без размеров. Но вклад сторонних скриптов виден по эксперименту выше: одни они стоили 23 балла.

По тому же принципу работает и этот сайт: Яндекс Метрика здесь запускается после первого действия посетителя.

Чем за это платим

  • Короткие визиты без действий. Если человек открыл страницу и закрыл её через секунду, ничего не тронув, аналитика может его не засчитать. Для большинства сайтов это несколько процентов самых коротких отказов — смиритесь или сравните цифры до и после внедрения.
  • Виджеты появляются не сразу. Кнопка обратного звонка выезжает через пару секунд после первого движения. Для посетителя разница незаметна: он всё равно сначала смотрит на страницу.

Это не обман проверки. Посетитель и PageSpeed получают одну и ту же страницу: и у того, и у другого сторонние скрипты стартуют после первого действия или через 3,5 секунды после загрузки. Мы не прячем код от проверки — мы откладываем его для всех.

Другие сторонние ресурсы

  • Карты. API карт — сотни килобайт и заметная блокировка потока. Подключайте на странице контактов и только когда блок карты приближается к экрану; подробно — в следующей части цикла.
  • Видео с YouTube, VK, Rutube. Встроенный плеер тянет мегабайты ещё до нажатия «плей». Вместо плеера показывайте картинку-превью и подставляйте iframe по клику — об этом тоже отдельная часть.
  • Чаты и квизы. Тот же подход, что у виджета звонка: после первого действия.
А async и defer на скриптах не помогают?

Они разрешают браузеру не ждать скрипт при разборе страницы, но скрипт всё равно загрузится и выполнится в первые секунды — внутри окна, которое оценивает PageSpeed, и с той же нагрузкой на процессор. Отложенная загрузка по действию выносит его за это окно.

Может, лучше подключить всё через Google Tag Manager?

Менеджер тегов — ещё один сторонний скрипт, который грузит остальные. Удобно для маркетинга, но для скорости это не решение, а ещё одна задача в главном потоке. Его тоже можно отложить тем же способом.

Сторонние виджеты тормозят сайт?

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

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

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

Комментарии

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

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