Отложенная загрузка скриптов и шрифтов в WordPress: lazy-load и defer

Отложенная загрузка скриптов и шрифтов в WordPress: lazy-load и defer

Кратко: отложенная загрузка — это приёмы, которые освобождают первый экран от второстепенных ресурсов: defer и async переносят выполнение скриптов на потом, loading=»lazy» откладывает загрузку изображений и фреймов до прокрутки, а font-display: swap убирает невидимый текст при загрузке шрифтов. В WordPress это настраивается плагином, фильтром script_loader_tag, параметром strategy у wp_enqueue_script или подгрузкой скрипта по событию. Ниже — рабочие примеры PHP и JavaScript, сравнительные таблицы и чек-лист проверки результата.

Почему скрипты тормозят загрузку: как браузер обрабатывает JavaScript

Когда браузер встречает в HTML обычный тег script, он останавливает разбор страницы, скачивает файл, выполняет его и только затем продолжает строить DOM. Это называется блокировкой парсера: пока грузится скрипт, пользователь смотрит на пустой экран. Если таких файлов десять, задержки складываются, и страница оживает заметно позже, чем могла бы.

<script src="/wp-content/plugins/my-plugin/assets/script.js"></script>

В WordPress к скриптам темы добавляются файлы плагинов: слайдер, формы, галерея, всплывающие окна, счётчики, виджет чата. Каждый подключается через wp_enqueue_script, и по умолчанию все они загружаются синхронно. Основы правильного подключения разобраны в статье про подключение CSS и JS в WordPress через wp_enqueue_scripts, а здесь мы займёмся именно отложенной загрузкой: как перенести выполнение на потом и ничего не сломать.

Со стилями ситуация похожая, но ещё строже: CSS блокирует первую отрисовку целиком. Если стили уже оптимизированы по правилам критического CSS для первой отрисовки, логичный следующий шаг — разобраться со скриптами. С них и начнём.

Ещё один источник проблем — подгрузка «тяжёлых» библиотек ради мелких задач: jQuery-плагин для одной анимации, внешний виджет карты на главной, скрипт соцсетей в каждой статье. Каждый такой файл конкурирует за канал и откладывает момент, когда сайт становится интерактивным. Отложенная загрузка не уменьшает вес ресурсов — она меняет порядок работ так, чтобы приоритетное загружалось раньше второстепенного.

defer и async: разница, которую важно понимать

Два атрибута решают проблему блокировки по-разному. async говорит браузеру: скачивай файл параллельно с разбором и выполняй, как только он загрузится. defer — скачивай параллельно, но выполняй после того, как DOM построен, и обязательно в том порядке, в котором скрипты подключены.

Подключение Когда скачивается Когда выполняется Порядок Кому подходит
Без атрибутов При разборе HTML, с паузой Сразу после загрузки файла Сохраняется Скриптам, которые нужны до первой отрисовки
async Параллельно, без пауз Как только файл скачается Не гарантируется Аналитике, счётчикам, экспериментам
defer Параллельно, без пауз После построения DOM, до DOMContentLoaded Сохраняется Интерфейсу темы и плагинам

Из таблицы вытекают простые правила. Всё, что относится к интерфейсу темы — меню, мобильная навигация, слайдеры, формы, галереи — почти всегда подходит под defer: им нужен готовый DOM, и порядок подключения для них важен. Независимые счётчики и аналитику лучше переводить на async: они не связаны с разметкой и не должны никого ждать. А вот скрипты, которые обязаны выполниться до первой отрисовки, трогать не нужно — они редки, но существуют.

<script defer src="/wp-content/themes/my-theme/js/navigation.js"></script>
<script async src="https://analytics.example.com/tracker.js"></script>

Кстати, о третьем варианте: скрипты с type=»module» по умолчанию ведут себя как defer, и добавлять им атрибут не нужно. Отдельно скажем про распространённое заблуждение: «поставим defer всем скриптам, и всё ускорится». На практике важна связка «дескриптор и его зависимости»: один и тот же файл в одном проекте безопасно отложить, а в другом его вызывает инлайн-код прямо в HTML — и после откладывания получится ошибка. Поэтому подход всегда такой: сначала понять, кто зависит от скрипта, потом выбирать атрибут.

Отложенная загрузка без кода: плагины для WordPress

Самый быстрый способ увидеть эффект — подключить плагин оптимизации. Все популярные решения умеют одно и то же ядро: агрегировать файлы, добавлять defer и async, откладывать выполнение «тяжёлых» скриптов до взаимодействия. Отличаются они глубиной настроек и наличием менеджера скриптов по страницам.

Плагин Что умеет Когда выбирать
Autoptimize Агрегация и минификация JS, вынос скриптов в футер, флаги defer и async Когда нужен рабочий результат без бюджета
WP Rocket Кэш и оптимизация вместе: отложенное выполнение JavaScript, стратегии загрузки, ленивая загрузка Когда нужен комбайн из коробки
Perfmatters Менеджер скриптов: отключение и отложенная загрузка файлов точечно, по страницам Когда базовая оптимизация уже сделана и нужны детали
Clearfy Бесплатный комплекс оптимизаций, включая отложенную загрузку JavaScript Когда хочется собрать полезные настройки в одном месте

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

wp plugin install autoptimize --activate

Другие полезные команды WP-CLI — в гайде про WP-CLI для новичков. И не забывайте: минификация и объединение файлов дополняют отложенную загрузку, но не заменяют её — подробности в статье про минификацию и объединение CSS и JS.

Отложенная загрузка скриптов кодом: фильтры и параметр strategy

Если плагин не подходит, атрибуты добавляются двумя способами: при регистрации собственного скрипта и фильтром для чужих. Начнём с правильного — со своего файла. С версии WordPress 6.3 функция wp_enqueue_script принимает массив аргументов, в котором можно сразу указать стратегию загрузки.

<?php
add_action('wp_enqueue_scripts', function () {
    wp_enqueue_script(
        'my-theme-nav',
        get_stylesheet_directory_uri() . '/js/navigation.js',
        array(),
        '1.0.0',
        array('strategy' => 'defer', 'in_footer' => true)
    );
});

Допустимые значения strategy — defer и async. Для скриптов плагинов, которые нельзя переподключить, остаётся фильтр script_loader_tag: он переписывает готовый тег script перед выводом в разметку.

<?php
add_filter('script_loader_tag', function ($tag, $handle) {
    if (is_admin()) {
        return $tag;
    }
    $defer = array('my-theme-nav', 'my-theme-slider', 'my-theme-lightbox');
    if (in_array($handle, $defer, true)) {
        return str_replace(' src=', ' defer src=', $tag);
    }
    $async = array('yandex-metrika');
    if (in_array($handle, $async, true)) {
        return str_replace(' src=', ' async src=', $tag);
    }
    return $tag;
}, 10, 2);

Как узнать дескриптор? Посмотрите исходный код страницы: у каждого тега есть id вида my-theme-nav-js — отбросьте суффикс -js, и получите имя для фильтра. Сниппеты удобно хранить в functions.php дочерней темы — так обновление основного шаблона их не затрёт; как её сделать, рассказано в гайде про дочернюю тему WordPress.

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

Lazy-load изображений и фреймов: нативный loading=»lazy»

Со временем WordPress научился ленивой загрузке из коробки: изображениям с указанными размерами ядро само добавляет атрибут loading=»lazy», а фреймам — например, вставкам видео с YouTube — тот же атрибут ставится при фильтрации контента. Само по себе это уже экономит трафик и ускоряет первую отрисовку: браузер не тратит канал на то, что находится за пределами экрана.

Проблема появляется, когда под ленивую загрузку попадает главное изображение страницы. Браузер откладывает его загрузку до расчёта разметки, и Largest Contentful Paint ухудшается: посетитель дольше ждёт картинку, которая формирует первое впечатление. Поэтому главное изображение — обложку записи, баннер на главной, логотип — стоит исключать из ленивой загрузки и помечать атрибутом fetchpriority со значением high.

Исключить отдельное изображение можно фильтром, который ищет его по имени файла. Пример ниже пропускает через себя все теги с автоматическим атрибутом и возвращает false для логотипа.

<?php
add_filter('wp_img_tag_add_loading_attr', function ($value, $image, $context) {
    if (false !== strpos($image, 'logo')) {
        return false;
    }
    return $value;
}, 10, 3);

В собственной разметке, которую вы выводите вручную — в шаблоне, шорткоде или блоке, — атрибут ставится напрямую. Заодно укажите размеры и decoding, чтобы браузер резервировал место и не дёргал вёрстку.

<img src="/wp-content/uploads/2026/09/katalog-tovarov.jpg" alt="Каталог товаров после оптимизации" loading="lazy" decoding="async" width="1200" height="800">

Логичное продолжение работы с медиа — сжатие файлов и современные форматы: как перейти на WebP и настроить адаптивные картинки, мы рассказывали в статье про оптимизацию изображений в WordPress.

Подгрузка JavaScript по событию: паттерн delayed loading

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

(function () {
  var loaded = false;
  function loadWidget() {
    if (loaded) { return; }
    loaded = true;
    var s = document.createElement('script');
    s.src = '/wp-content/plugins/my-chat/assets/widget.js';
    s.defer = true;
    document.body.appendChild(s);
  }
  ['scroll', 'mousemove', 'touchstart', 'keydown'].forEach(function (event) {
    window.addEventListener(event, loadWidget, { once: true, passive: true });
  });
  window.setTimeout(loadWidget, 8000);
})();

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

Если хочется обойтись без кода, у WP Rocket и Perfmatters есть опция Delay JavaScript Execution: вы указываете скрипты, которые должны выполниться после первого взаимодействия, и плагин сам расставляет задержки. Механизм тот же, просто упакованный в интерфейс — начните со списка из виджетов и аналитики, а не со всех файлов сразу.

Шрифты: font-display, preload и локальный хостинг

Шрифты тормозят страницу двумя способами. Первый — подключение через внешний сервис: пока не загрузится CSS шрифтов, текст может оставаться невидимым. Второй — вес файлов: несколько начертаний по 100–200 КБ каждое конкурируют за канал с остальными ресурсами.

Лечится это тремя шагами. Первый — правило font-display: swap в описании шрифта: браузер сразу рисует текст системным шрифтом и подменяет его, когда файл догрузился. Второй — предзагрузка самых нужных начертаний через тег preload, чтобы они скачивались с высоким приоритетом. Третий — локальный хостинг вместо внешних сервисов: файлы кладутся в тему, загрузка ускоряется, а внешних зависимостей становится меньше.

@font-face {
  font-family: 'Inter';
  src: url('/wp-content/themes/my-theme/fonts/inter-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

В дочернюю тему файлы удобно положить рядом с CSS, а предзагрузку добавить в head через свой хук. Обратите внимание на атрибут crossorigin: без него браузер скачает файл дважды.

<link rel="preload" href="/wp-content/themes/my-theme/fonts/inter-400.woff2" as="font" type="font/woff2" crossorigin="anonymous">

Тот же результат, если вывод удобнее держать в PHP.

<?php
add_action('wp_head', function () {
    $font = get_stylesheet_directory_uri() . '/fonts/inter-400.woff2';
    echo '<link rel="preload" href="' . esc_url($font) . '" as="font" type="font/woff2" crossorigin="anonymous">';
}, 1);

Не предзагружайте больше двух-трёх начертаний: каждая запись preload — это высокий приоритет, и десяток таких записей задушит остальную загрузку. Основной текст вполне обходится одним-двумя весами, а остальные пусть подтягиваются в фоне.

Как проверить результат: инструменты и метрики

Отложенная загрузка меняет порядок сетевых запросов, поэтому проверять её нужно не на глаз, а приборами. Базовый набор: браузерные DevTools и Lighthouse; для глубокого разбора — WebPageTest. Сравнивайте «до и после» на одинаковых условиях: режим инкогнито, очищенный кэш, включённое ограничение скорости сети.

Инструмент Что проверяем Ориентир
PageSpeed Insights и Lighthouse Блокирующие ресурсы, Total Blocking Time, LCP Предупреждений о скриптах нет или они короче, TBT снижается
DevTools, вкладка Network Когда начинается загрузка скриптов и фреймов Виджеты грузятся после взаимодействия, а не в первых запросах
DevTools, вкладка Coverage Доля неиспользованного JavaScript на старте Стартовая загрузка худеет, «мусорный» код уходит в отложенные файлы
WebPageTest Водопад запросов и раскадровка загрузки Первый экран рисуется до тяжёлых скриптов

Быстрая проверка в консоли — посмотреть, появляются ли долгие задачи при загрузке: это куски JavaScript, которые блокируют главный поток и мешают странице реагировать.

new PerformanceObserver(function (list) {
  list.getEntries().forEach(function (entry) {
    console.log('Долгая задача:', Math.round(entry.duration), 'мс');
  });
}).observe({ type: 'longtask', buffered: true });

Полноценный отчёт собирается одной командой — это тот же движок, что работает в PageSpeed Insights.

npx lighthouse https://example.com --only-categories=performance --preset=desktop --view

В отчёте смотрите на три вещи: исчезли ли предупреждения о блокирующих скриптах, сократился ли Total Blocking Time и не появилось ли новых проблем с вёрсткой. И помните про кэш: после правок очистите и серверный, и браузерный кэш, иначе будете тестировать старую версию файлов — общие принципы собраны в статье про кэширование WordPress.

Типичные ошибки и подводные камни

defer для jQuery-зависимых скриптов. Если отложить библиотеку, но не её «спутники», исполнится не то и не тогда: зависимый файл может обогнать jQuery. Проверяйте цепочки зависимостей и переводите связку целиком, следя за сохранением порядка.

Инлайн-код против defer. Маленький блок кода в HTML выполняется во время разбора страницы, а отложенный файл — после. Если инлайн-код вызывает функцию из файла, будет ошибка «функция не определена». Такие инициализации нужно переносить в отложенный файл или оборачивать в событие DOMContentLoaded.

Двойная оптимизация. Плагин уже добавил defer, а вы добавили его ещё раз фильтром. Дубли обычно безобидны, но ломают диагностику: непонятно, чей именно атрибут сработал. Перед добавлением проверьте, не управляет ли этим же скриптом плагин.

Ленивая загрузка главного изображения. Обложка статьи и баннер главной — не место для loading=»lazy»: LCP ухудшается, пользователь видит пустой блок. Главные изображения грузите сразу, с fetchpriority high, а лениво — только то, что ниже первого экрана.

Delay для критичной функциональности. Откладывать до взаимодействия корзину, калькулятор или форму заказа нельзя: человек нажмёт кнопку раньше, чем подгрузится скрипт, и решит, что сайт сломан. Список отложенного держите коротким: чаты, карты, соцвиджеты.

Предзагрузка всего подряд. Десяток preload шрифтов и скриптов — это очередь с высоким приоритетом, которая тормозит важные ресурсы. Предзагружайте один-два ключевых ресурса, остальное отдайте браузеру.

Тестирование со старым кэшем. После каждой правки очищайте кэш и проверяйте сайт в инкогнито: иначе выводы будут о прошлой версии. Если после изменений что-то пошло не так, включите отладку по инструкции WP_DEBUG и отладка WordPress и посмотрите логи — ошибки в хуках видно сразу.

Правки без бэкапа и обновления наспех. Скрипты плагинов меняются при обновлениях, поэтому держите свежие копии: как настроить резервное копирование WordPress, мы уже рассказывали. А сами обновления ставьте по правилам безопасного обновления WordPress — и тогда оптимизация не превратится в ремонт.

TimeWeb

Частые вопросы об отложенной загрузке в WordPress

Что такое отложенная загрузка простыми словами?

Это приёмы, которые освобождают первый экран от второстепенных ресурсов. Скрипты со стратегиями defer и async не блокируют построение страницы, изображения и фреймы с атрибутом lazy загружаются по мере прокрутки, шрифты появляются в тексте без невидимой паузы. В сумме это даёт более быстрый старт и лучшие метрики, чем обычная синхронная загрузка.

В чём разница между defer и async?

Оба атрибута скачивают скрипт параллельно с разбором страницы. Разница в выполнении: defer запускает файл после построения DOM и сохраняет порядок подключения, async выполняет сразу после загрузки, порядок при этом не гарантируется. Практический вывод: defer — для скриптов темы и плагинов, которым важны DOM и последовательность, async — для независимых счётчиков и аналитики.

Можно ли включить отложенную загрузку без кода?

Да. Плагины Autoptimize, WP Rocket, Perfmatters и Clearfy умеют добавлять стратегии загрузки и откладывать выполнение JavaScript без единой строки кода. Достаточно включить нужные опции, очистить кэш и проверить меню, формы и слайдеры. Начните с базового набора настроек и добавляйте тонкие опции только после тестов.

Почему после добавления defer что-то перестало работать?

Почти всегда это порядок выполнения: зависимый скрипт обогнал основной или инлайн-код в HTML выполнился раньше отложенного файла. Уберите атрибут у проблемного дескриптора, проверьте зависимости при подключении скриптов и перенесите инлайн-инициализацию в отдельный файл. Точечная отладка через WP_DEBUG быстро покажет виновника.

Стоит ли включать ленивую загрузку для всех изображений?

Нет. Ленивая загрузка нужна изображениям ниже первого экрана, а главную картинку страницы — обложку записи, баннер, логотип — лучше грузить сразу, иначе Largest Contentful Paint ухудшится. Для главных изображений оставляйте обычную загрузку и добавляйте fetchpriority со значением high, а атрибут lazy — для всего, что появляется после прокрутки.

Как проверить, что скрипты загружаются отложенно?

Откройте вкладку Network в DevTools: у отложенных скриптов загрузка начнётся после построения страницы или после вашего действия, а не в первых строках водопада. Затем прогоните страницу в Lighthouse и сравните блокирующие ресурсы и метрики до и после. Финальная проверка — вручную пройти ключевые сценарии в режиме инкогнито.

Вывод

Отложенная загрузка — это не один приём, а порядок в ресурсах: defer и async убирают блокировку из скриптов, loading=»lazy» переносит картинки и фреймы за пределы первого экрана, font-display и preload наводят порядок в шрифтах, а паттерн delayed loading избавляет от виджетов до первого взаимодействия. Каждый шаг делается маленькими правками и проверяется инструментами.

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

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

Clearfy

Поделиться:
Telegram ВКонтакте