Подключение CSS и JS в WordPress: wp_enqueue_scripts

Подключение CSS и JS в WordPress: wp_enqueue_scripts

Кратко: Стили и скрипты в WordPress подключают не напрямую в шаблоне, а через функции wp_enqueue_style() и wp_enqueue_script() внутри хука wp_enqueue_scripts, который вызывают из functions.php темы или из файла плагина. Такой способ даёт WordPress знать обо всех файлах: он сам выстраивает очередь загрузки, учитывает зависимости, добавляет версию к адресу файла и отдаёт готовый список плагинам кэширования и минификации. Прямое вписывание <link> и <script> в header.php приводит к дублям, конфликтам и сломанной оптимизации. Ниже — готовые примеры кода для темы и плагина, разбор параметров зависимостей и версий, условная загрузка файлов только на нужных страницах, передача данных из PHP в JavaScript через wp_localize_script() и отключение лишних стилей и скриптов.

Что такое wp_enqueue_scripts и почему нельзя вставлять ссылки прямо в header.php

В WordPress за подключение файлов отвечает механизм под названием «enqueue» — буквально «поставить в очередь». Вместо того чтобы жёстко прописывать адреса файлов в шаблоне, вы сообщаете ядру: «нужно подключить вот этот CSS и вот этот JS». Ядро собирает все заявки от темы и плагинов, сортирует их с учётом зависимостей и выводит в итоговом HTML в правильном порядке.

Заявки на фронтенде принимает хук wp_enqueue_scripts. Он срабатывает в момент, когда WordPress готов формировать страницу, и именно к нему принято привязывать свои функции. Новички часто путают название хука с названием одноимённой функции — это разные вещи: хук wp_enqueue_scripts вызывается один раз, а внутри него вы уже используете функции wp_enqueue_style() и wp_enqueue_script() столько раз, сколько нужно файлов.

Казалось бы, зачем усложнять? Проще открыть header.php и написать адрес файла руками. Но у такого подхода есть целый набор проблем:

  • Дубли. Если тот же файл подключает ещё и плагин, в коде появится две одинаковые ссылки, а браузер скачает файл дважды или применит стили в неожиданном порядке.
  • Нет управления зависимостями. Вы не можете сказать «загрузи этот скрипт только после jQuery» — придётся вручную следить за порядком тегов, и любая ошибка ломает JavaScript.
  • Нет версионирования. После обновления файла браузеры и CDN продолжают отдавать старую версию из кэша, и пользователи видят устаревший дизайн.
  • Ломается кэширование и минификация. Плагины вроде Clearfy, Autoptimize или WP Rocket умеют объединять и сжимать файлы, но видят только те, что прошли через очередь. Рукописные теги они не трогают.
  • Теряется гибкость. Отключить или заменить чужой файл можно только через очередь — wp_dequeue_style() не найдёт то, что вы вписали в шаблон вручную.

Простой пример «плохого» кода, который иногда встречается в самодельных темах:

<link rel="stylesheet" href="/wp-content/themes/my-theme/assets/css/main.css">
<script src="/wp-content/themes/my-theme/assets/js/main.js"></script>

Если тема — дочерняя или планируется обновление, такой путь ещё и оторвётся от реальной папки темы. Правильный путь всегда строится через функции WordPress: get_stylesheet_uri(), get_template_directory_uri(), plugin_dir_url(). Подробнее о структуре тем и о том, зачем нужен отдельный дочерний вариант, рассказано в статье про дочернюю тему WordPress.

Три функции, которые нужно знать: enqueue, register и их параметры

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

wp_enqueue_style( $handle, $src, $deps, $ver, $media ) — подключает CSS-файл. Обработчик (handle) — это уникальное имя файла, по которому WordPress отличает один ресурс от другого. Источник (src) — ссылка на файл. Зависимости (deps) — массив имён других стилей, которые должны загрузиться раньше. Версия (ver) добавляется к адресу в виде ?ver=1.0.0 и сбрасывает кэш. Media задаёт тип носителя, например all, screen или print.

wp_enqueue_script( $handle, $src, $deps, $ver, $in_footer ) — подключает JavaScript. Отличий два: последний параметр $in_footer отвечает за место вывода (в голове или в конце страницы), а зависимости задаются не только вашими скриптами, но и встроенными — например, jquery.

wp_register_style() и wp_register_script() — «регистрируют» файл, но не выводят его. Это удобно, когда нужно один раз описать ресурс, а подключать его позже при определённых условиях: зарегистрировали в начале, вывели только на нужной странице.

Важно понимать, что обработчики должны быть уникальными. Если назвать свой файл style, легко перетереть чужой ресурс или создать дубль — префикс вида mytheme- или myplugin- решает эту проблему. Полный список правил и подводных камней работы с функциями темы удобно держать под рукой — часть из них собрана в заметке про полезные сниппеты для functions.php.

Подключение CSS и JS в тему: полный пример для functions.php

Начнём с самого частого случая — своя тема. Откройте файл functions.php и добавьте функцию подключения. Ниже рабочий пример, в котором подключаются базовый стиль темы, дополнительный CSS и один JS-файл с зависимостью от jQuery:

<?php
/**
 * Подключаем стили и скрипты темы.
 */
function mytheme_enqueue_assets() {

    // Главный файл style.css темы.
    wp_enqueue_style(
        'mytheme-style',
        get_stylesheet_uri(),
        array(),
        '1.0.0'
    );

    // Дополнительный CSS, который зависит от главного.
    wp_enqueue_style(
        'mytheme-main',
        get_template_directory_uri() . '/assets/css/main.css',
        array( 'mytheme-style' ),
        '1.0.0'
    );

    // Скрипт, который подключается после jQuery и грузится в подвале.
    wp_enqueue_script(
        'mytheme-script',
        get_template_directory_uri() . '/assets/js/main.js',
        array( 'jquery' ),
        '1.0.0',
        true
    );
}
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_assets' );

Обратите внимание на разницу между двумя функциями путей. get_stylesheet_uri() возвращает адрес главного файла style.css активной темы — это стандартное требование WordPress, и без такого подключения тема может считаться «сломанной» в проверках. А get_template_directory_uri() возвращает адрес папки родительской темы, что важно, если вы работаете с дочерней темой: там требуется get_stylesheet_directory_uri(), чтобы файлы брались из папки дочерней темы.

После сохранения файла зайдите на сайт и откройте исходный код страницы. Внутри <head> должны появиться строки вида <link rel="stylesheet" ... href=".../main.css?ver=1.0.0">, а перед закрывающим </body> — тег <script src=".../main.js?ver=1.0.0">. Именно наличие ?ver= подтверждает, что файл подключён через очередь, а не вручную.

Если что-то не появилось, проверьте три вещи: функцию действительно вызывает add_action() для хука wp_enqueue_scripts; адрес файла ведёт к существующей папке; обработчик не повторяет имя уже существующего ресурса. Ещё одна частая причина — правка родительской темы: при обновлении темы ваши изменения затрутся. Поэтому код удобнее держать в дочерней теме или в виде своего маленького плагина. О том, как правильно обновлять тему, чтобы ничего не потерять, есть отдельный разбор — безопасное обновление WordPress.

Как подключить стили и скрипты внутри плагина

В плагине логика та же, но меняется способ построения пути. Здесь удобно использовать функцию plugin_dir_url( __FILE__ ), которая возвращает адрес папки плагина с завершающим слэшем:

<?php
/**
 * Подключаем ресурсы плагина.
 */
function myplugin_enqueue_assets() {

    wp_enqueue_style(
        'myplugin-style',
        plugin_dir_url( __FILE__ ) . 'assets/style.css',
        array(),
        '2.1.0'
    );

    wp_enqueue_script(
        'myplugin-script',
        plugin_dir_url( __FILE__ ) . 'assets/script.js',
        array( 'jquery' ),
        '2.1.0',
        true
    );
}
add_action( 'wp_enqueue_scripts', 'myplugin_enqueue_assets' );

Стоит запомнить простое правило: plugin_dir_url() для плагинов, get_template_directory_uri() для родительской темы и get_stylesheet_directory_uri() для дочерней. Смешивать их нельзя — из-за этого ссылки ломаются после переноса сайта или смены темы.

Отдельный нюанс — стили для административной части. Если вы делаете настройки плагина в панели управления, для админки используется другой хук: admin_enqueue_scripts. Он выглядит почти так же, но подключается только в /wp-admin/, поэтому не утяжеляет публичные страницы. Типовая конструкция такая:

<?php
function myplugin_admin_assets( $hook ) {
    // Подключаем только на странице настроек плагина.
    if ( 'settings_page_myplugin' !== $hook ) {
        return;
    }

    wp_enqueue_style(
        'myplugin-admin',
        plugin_dir_url( __FILE__ ) . 'assets/admin.css',
        array(),
        '2.1.0'
    );
}
add_action( 'admin_enqueue_scripts', 'myplugin_admin_assets' );

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

Зависимости, версии и загрузка в подвале: разбираем параметры

Теперь подробнее про три параметра, которые чаще всего вызывают вопросы: зависимости, версию и место вывода.

Зависимости (deps). Массив обработчиков, которые должны быть подключены раньше вашего файла. WordPress выстраивает очередь так, что зависимость всегда идёт первой, даже если вы вызвали свой ресурс выше по коду. Пример: скрипт-слайдер требует jQuery, поэтому его зависимости — array( 'jquery' ). Стили тоже могут зависеть друг от друга: например, дополнительный CSS подключается после основного, чтобы его правила перекрывали базовые.

Версия (ver). Строка, которая добавляется к адресу файла как параметр ?ver=. Для разработки удобно указывать номер вручную и поднимать его при изменениях. Если версию не указывать (или передать null), WordPress подставит версию ядра — и после обновления CMS кэш браузера не сбросится. Профессиональный приём — брать время изменения файла:

<?php
$file = get_template_directory() . '/assets/css/main.css';
$ver  = file_exists( $file ) ? filemtime( $file ) : '1.0.0';

wp_enqueue_style(
    'mytheme-main',
    get_template_directory_uri() . '/assets/css/main.css',
    array(),
    $ver
);

Место вывода ($in_footer). Если поставить true, скрипт подключится перед закрывающим тегом </body>, а не в <head>. Это ускоряет отрисовку страницы: браузер не ждёт загрузки JavaScript, чтобы показать текст. Есть лишь одно ограничение — скрипт, который должен отработать до появления разметки, придётся оставить в голове. Для обычных скриптов темы значение true — хороший выбор по умолчанию.

Иногда нужно добавить не сам файл, а небольшой фрагмент кода. Для этого есть wp_add_inline_style() и wp_add_inline_script() — они привязывают строку к уже зарегистрированному ресурсу и выводят её рядом с ним. Так вы не создаёте лишних файлов и не нарушаете порядок загрузки.

Условная загрузка: подключаем файлы только там, где они нужны

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

<?php
function mytheme_conditional_assets() {

    // Стили только для одиночной записи блога.
    if ( is_singular( 'post' ) ) {
        wp_enqueue_style(
            'mytheme-single',
            get_template_directory_uri() . '/assets/css/single.css',
            array( 'mytheme-main' ),
            '1.0.0'
        );
    }

    // Скрипт карты только для шаблона контактов.
    if ( is_page_template( 'page-contacts.php' ) ) {
        wp_enqueue_script(
            'mytheme-map',
            get_template_directory_uri() . '/assets/js/map.js',
            array(),
            '1.0.0',
            true
        );
    }

    // Стили магазина только внутри WooCommerce.
    if ( function_exists( 'is_woocommerce' ) && is_woocommerce() ) {
        wp_enqueue_style(
            'mytheme-shop',
            get_template_directory_uri() . '/assets/css/shop.css',
            array(),
            '1.0.0'
        );
    }
}
add_action( 'wp_enqueue_scripts', 'mytheme_conditional_assets' );

Самые полезные проверки: is_front_page() для главной, is_home() для страницы записей, is_singular( 'post' ) для одной записи, is_page() для статических страниц, is_page_template() для конкретного шаблона, is_archive() для архивов и is_search() для результатов поиска. Перед вызовом функций плагинов всегда добавляйте проверку function_exists() — иначе при отключённом плагине сайт выдаст ошибку.

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

Есть и обратная задача: свои скрипты, которые должны работать в редакторе блоков. Они подключаются через хук enqueue_block_editor_assets. Это отдельная тема, тесно связанная с разработкой собственных блоков — базовый пример есть в гайде про свои блоки Gutenberg.

Передача данных из PHP в JavaScript: wp_localize_script и не только

Почти любому скрипту нужны данные из PHP: адрес AJAX-обработчика, одноразовый ключ безопасности (nonce), настройки, переведённые строки. Писать их прямо в JS нельзя, а вывод через echo в шаблоне — плохая практика. Правильный инструмент — wp_localize_script(). Он принимает обработчик скрипта, имя глобального объекта и массив данных, а затем выводит их прямо перед вашим файлом:

<?php
function mytheme_enqueue_ajax() {

    wp_enqueue_script(
        'mytheme-ajax',
        get_template_directory_uri() . '/assets/js/ajax.js',
        array( 'jquery' ),
        '1.0.0',
        true
    );

    // Передаём данные в JS через объект myThemeData.
    wp_localize_script( 'mytheme-ajax', 'myThemeData', array(
        'ajaxUrl' => admin_url( 'admin-ajax.php' ),
        'nonce'   => wp_create_nonce( 'mytheme_nonce' ),
    ) );
}
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_ajax' );

Теперь в JavaScript доступен объект myThemeData с готовыми значениями. Типовая задача — подгрузка записей без перезагрузки страницы:

jQuery( function ( $ ) {
    $( '#load-more' ).on( 'click', function () {
        $.post( myThemeData.ajaxUrl, {
            action: 'mytheme_load_more',
            nonce:  myThemeData.nonce,
            page:   2
        }, function ( response ) {
            $( '#posts' ).append( response );
        } );
    } );
} );

Обратите внимание: nonce здесь не украшение, а обязательная защита. Без него любой сможет дёргать ваш AJAX-обработчик со стороны, а на сервере проверка check_ajax_referer() просто не сработает. Как связать такой скрипт с серверной функцией, пошагово разобрано в статье про AJAX в WordPress через admin-ajax.

Если данные нужны как обычная переменная, а не объект, или объект создаётся позже, используется другой приём. С версии 5.7 у функции wp_localize_script() появился аккуратный «сосед» — wp_add_inline_script() с кодированием через wp_json_encode():

<?php
wp_add_inline_script(
    'mytheme-ajax',
    'window.myThemeConfig = ' . wp_json_encode( array( 'perPage' => 6 ) ) . ';',
    'before'
);

Такой способ считается более современным: он не превращает числа в строки и корректно экранирует кавычки и спецсимволы. А если данные вообще не нужны в момент загрузки, а запрашиваются с сервера уже после открытия страницы, стоит присмотреться к REST API — об этом есть отдельный разбор про REST API WordPress.

Как отключить лишние стили и скрипты: wp_dequeue и wp_deregister

Иногда задача обратная: убрать то, что грузит тема или плагин. Стили и скрипты, прошедшие через очередь, можно «вынуть» обратно. Делают это на том же хуке, но с высоким приоритетом — чтобы ваш код выполнился после чужого:

<?php
function mytheme_cleanup_assets() {

    // Отключаем встраивание записей и скрипт эмодзи.
    wp_dequeue_script( 'wp-embed' );

    // Убираем ресурсы плагина там, где он не нужен.
    if ( ! is_singular( 'product' ) ) {
        wp_dequeue_style( 'myplugin-style' );
        wp_dequeue_script( 'myplugin-script' );
    }
}
add_action( 'wp_enqueue_scripts', 'mytheme_cleanup_assets', 100 );

Разница между функциями проста: wp_dequeue_* убирает файл из очереди на текущей странице, но оставляет его зарегистрированным, а wp_deregister_* удаляет регистрацию полностью. Для точечного отключения почти всегда достаточно первого варианта. Важная оговорка: чтобы отключить чужой ресурс, нужно точно знать его обработчик. Проще всего найти его в исходном коде страницы по атрибуту id у тега — WordPress формирует его как имя-обработчика-css или имя-обработчика-js.

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

И последнее про порядок. Стили выводятся в том порядке, в котором они попали в очередь, а зависимости всегда сдвигают ресурс вперёд. Поэтому вместо десятков одинаковых приоритетов (10, 20, 30) лучше честно указывать зависимости — это делает код самодокументируемым и устойчивым к перестановкам. Механизм приоритетов подробно разобран в статье про хуки actions и filters в WordPress.

Таблица: сравнение способов подключения и частые ошибки

Сведём способы подключения файлов в одну таблицу, чтобы было проще выбрать правильный и понять, чем плохи остальные.

Способ Где применяется Зависимости и версии Работает с кэш-плагинами Риск дублей
wp_enqueue_style() / wp_enqueue_script() Тема и плагин, все фронтенд-файлы Да, полностью управляются Да Нет
wp_register_*() + wp_enqueue_*() Один раз зарегистрировать, вывести по условию Да Да Нет
wp_add_inline_style() / wp_add_inline_script() Небольшие фрагменты и настройки рядом с файлом Наследуются от родительского ресурса Да Нет
Теги <link> и <script> в шаблоне Быстрые правки «здесь и сейчас» Нет Нет Высокий
Директива @import внутри CSS Устаревший способ подтягивать стили Нет Нет Средний

А теперь список ошибок, которые встречаются чаще всего:

  • Хук вызван неверно. add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_assets' ) без круглых скобок у имени функции. Со скобками WordPress выполнит функцию сразу и получит null.
  • Подключение на хуке init или wp_head. Файлы или не подключатся, или подключатся слишком поздно и без учёта зависимостей.
  • Неверный путь к файлу. Абсолютный путь сервера вместо URL: get_template_directory() вместо get_template_directory_uri().
  • Действие вместо фильтра. Стили и скрипты добавляют через add_action(). add_filter() здесь не работает — функция ничего не возвращает.
  • Одинаковые обработчики. Своё style и script без префикса перетирают чужие ресурсы.
  • Забытая версия. Без параметра ?ver= после обновления файла посетители видят старый CSS из кэша.

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

TimeWeb

Частые вопросы о подключении CSS и JS в WordPress

Чем wp_enqueue_style отличается от обычного тега link?

Тег <link> просто добавляет файл в HTML, и WordPress о нём не знает. Функция wp_enqueue_style() ставит файл в очередь: ядро учитывает зависимости, добавляет версию для сброса кэша и отдаёт полный список плагинам оптимизации. Поэтому через очередь можно подключать, отключать и переставлять файлы, а с ручным тегом всё это недоступно.

Почему стили не подключаются, хотя функция добавлена в functions.php?

Проверьте по порядку: функция действительно привязана к хуку wp_enqueue_scripts через add_action(); в add_action() имя функции указано без круглых скобок; путь к файлу построен через get_template_directory_uri() или plugin_dir_url(), а не абсолютным путём сервера; файл существует по указанному адресу. Частая причина — обработчик уже занят другим ресурсом с тем же именем.

Как подключить скрипт только на одной странице?

Внутри функции подключения используйте условные теги. Например, if ( is_page( 'contacts' ) ) — только на странице с ярлыком contacts, if ( is_front_page() ) — только на главной, if ( is_page_template( 'page-contacts.php' ) ) — только на страницах с вашим шаблоном. Так остальные страницы не будут загружать лишний файл.

Что делает параметр $in_footer и когда ставить true?

Если передать true, скрипт подключится перед закрывающим тегом </body> вместо <head>. Страница начинает отрисовываться, не дожидаясь загрузки JavaScript, поэтому визуально она открывается быстрее. Для обычных скриптов темы это рекомендуемое значение. Оставлять в голове стоит только код, который обязан выполниться до появления разметки.

Можно ли подключать свои файлы через дочернюю тему?

Да, и это правильный подход. В дочерней теме пути строят через get_stylesheet_directory_uri() и get_stylesheet_directory() — они указывают на папку дочерней темы. Так при обновлении родительской темы ваши функции и файлы сохраняются, а изменения не затираются.

В каком порядке WordPress выводит подключённые файлы?

Сначала учитываются зависимости: ресурс, указанный в $deps, всегда выводится раньше. При равных условиях порядок определяется приоритетом хука: чем меньше число в третьем аргументе add_action(), тем раньше выполняется функция. Управлять порядком лучше через зависимости — это надёжнее, чем выставлять приоритеты вручную.

Вывод

Подключение стилей и скриптов — тот базовый навык, с которого начинается любая работа с WordPress. Пока файлы добавляют через хук wp_enqueue_scripts и функции wp_enqueue_style() и wp_enqueue_script(), сайт остаётся управляемым: файлы не дублируются, обновляются по версии, корректно работают с кэшем и не мешают плагинам оптимизации. Как только появляется рукописный тег в шаблоне, вы теряете эту управляемость.

Запомните четыре простых правила: подключайте всё через очередь, указывайте зависимости вместо ручных приоритетов, стройте пути через get_template_directory_uri() или plugin_dir_url(), грузите файлы только на нужных страницах. Этого достаточно, чтобы код был чистым, а сайт — быстрым. Дальше логичный шаг — свои сниппеты в дочерней теме или отдельном плагине; подробнее про это в гайде про сниппеты для functions.php и в материале про шорткоды WordPress.

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

Clearfy

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