Одна из самых частых ошибок начинающих WordPress-разработчиков — подключение CSS и JS файлов напрямую через хардкод в header.php. Это ломает совместимость с плагинами, дублирует ресурсы и усложняет отладку. В WordPress есть правильный механизм — система enqueue, которая решает все эти проблемы. Разберём, как ей пользоваться на практике.
Основы: wp_enqueue_style и wp_enqueue_script
Все скрипты и стили в WordPress подключаются через хук wp_enqueue_scripts. Этот хук срабатывает на фронтенде сайта. Для админки используется admin_enqueue_scripts, для страницы логина — login_enqueue_scripts.
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_assets' );
function mytheme_enqueue_assets() {
// Подключение CSS
wp_enqueue_style(
'mytheme-main', // handle (уникальный идентификатор)
get_stylesheet_directory_uri() . '/css/main.css', // URL файла
array(), // зависимости
'1.2.0', // версия
'all' // media
);
// Подключение JS
wp_enqueue_script(
'mytheme-app', // handle
get_stylesheet_directory_uri() . '/js/app.js', // URL файла
array( 'jquery' ), // зависимости
'1.2.0', // версия
true // загрузка в footer
);
}
Параметр handle — это уникальное имя ресурса. Если два плагина подключат стиль с одним handle, WordPress загрузит его только один раз. Поэтому используйте префикс темы или плагина.
Последний параметр в wp_enqueue_script — булево значение $in_footer. Если true, скрипт загрузится перед закрывающим </body>, что улучшает скорость рендеринга страницы. Всегда ставьте true, если скрипт не нужен в <head>.
Зависимости и порядок загрузки
Третий параметр — массив зависимостей. WordPress автоматически выстроит правильный порядок загрузки. Если ваш скрипт использует jQuery и другую библиотеку, укажите их:
add_action( 'wp_enqueue_scripts', 'mytheme_slider_assets' );
function mytheme_slider_assets() {
// Сначала регистрируем библиотеку (не подключаем)
wp_register_script(
'swiper',
get_stylesheet_directory_uri() . '/js/swiper-bundle.min.js',
array(),
'11.0.0',
true
);
wp_register_style(
'swiper-css',
get_stylesheet_directory_uri() . '/css/swiper-bundle.min.css',
array(),
'11.0.0'
);
// Подключаем свой скрипт с зависимостями
wp_enqueue_script(
'mytheme-slider',
get_stylesheet_directory_uri() . '/js/slider-init.js',
array( 'swiper' ), // Swiper загрузится первым
'1.0.0',
true
);
// Стиль слайдера зависит от стилей Swiper
wp_enqueue_style(
'mytheme-slider-css',
get_stylesheet_directory_uri() . '/css/slider.css',
array( 'swiper-css' ),
'1.0.0'
);
}
Разница между wp_register_script и wp_enqueue_script: register только регистрирует ресурс в системе, enqueue — регистрирует и сразу ставит в очередь на подключение. Регистрация полезна, когда библиотека может понадобиться другим скриптам как зависимость.
Передача данных из PHP в JavaScript: wp_localize_script
Часто нужно передать в JS переменные из PHP — URL для AJAX-запросов, настройки, переводы. Для этого есть wp_localize_script и более новая wp_add_inline_script:
add_action( 'wp_enqueue_scripts', 'mytheme_localize' );
function mytheme_localize() {
wp_enqueue_script(
'mytheme-app',
get_stylesheet_directory_uri() . '/js/app.js',
array(),
'1.0.0',
true
);
// wp_localize_script — создаёт глобальный JS-объект
wp_localize_script( 'mytheme-app', 'MyThemeData', array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'mytheme_nonce' ),
'siteUrl' => home_url( '/' ),
'i18n' => array(
'loading' => __( 'Загрузка...', 'mytheme' ),
'error' => __( 'Произошла ошибка', 'mytheme' ),
),
) );
// Альтернатива — wp_add_inline_script (с WP 4.5)
wp_add_inline_script( 'mytheme-app',
'const SITE_CONFIG = ' . wp_json_encode( array(
'restUrl' => rest_url( 'mytheme/v1/' ),
'userId' => get_current_user_id(),
) ) . ';',
'before' // вставить ДО основного скрипта
);
}
В JavaScript эти данные доступны так:
// После wp_localize_script
console.log( MyThemeData.ajaxUrl ); // /wp-admin/admin-ajax.php
console.log( MyThemeData.i18n.loading ); // Загрузка...
// После wp_add_inline_script
console.log( SITE_CONFIG.restUrl ); // /wp-json/mytheme/v1/
wp_localize_script всегда преобразует значения в строки, что может быть проблемой с числами и булевыми значениями. wp_add_inline_script с wp_json_encode сохраняет типы данных.
Условная загрузка: подключаем ресурсы только там, где нужно
Загружать все скрипты на всех страницах — плохая практика. Слайдер нужен только на главной, скрипт комментариев — только на одиночных записях. Условная загрузка экономит ресурсы и ускоряет сайт:
add_action( 'wp_enqueue_scripts', 'mytheme_conditional_assets' );
function mytheme_conditional_assets() {
// Слайдер только на главной
if ( is_front_page() ) {
wp_enqueue_script( 'mytheme-slider', get_stylesheet_directory_uri() . '/js/slider.js', array(), '1.0', true );
wp_enqueue_style( 'mytheme-slider-css', get_stylesheet_directory_uri() . '/css/slider.css', array(), '1.0' );
}
// Галерея только в записях с шорткодом
if ( is_singular() ) {
global $post;
if ( has_shortcode( $post->post_content, 'gallery' ) ) {
wp_enqueue_script( 'mytheme-gallery', get_stylesheet_directory_uri() . '/js/gallery.js', array(), '1.0', true );
}
}
// Скрипт для WooCommerce-страниц
if ( function_exists( 'is_woocommerce' ) && is_woocommerce() ) {
wp_enqueue_script( 'mytheme-shop', get_stylesheet_directory_uri() . '/js/shop.js', array( 'jquery' ), '1.0', true );
}
// Стили для страницы контактов (по slug)
if ( is_page( 'contacts' ) ) {
wp_enqueue_style( 'mytheme-contacts', get_stylesheet_directory_uri() . '/css/contacts.css', array(), '1.0' );
}
}
Для ещё более гибкого управления ассетами на уровне каждой страницы используют плагины вроде Gonzales (Asset CleanUp) — он позволяет отключать ненужные CSS/JS прямо из редактора записи.
Отключение стандартных и плагинных ресурсов
Иногда нужно убрать лишние стили или скрипты, добавленные ядром или плагинами. Для этого используются функции wp_dequeue_style и wp_dequeue_script:
add_action( 'wp_enqueue_scripts', 'mytheme_dequeue_unwanted', 100 );
function mytheme_dequeue_unwanted() {
// Убираем стили блоков Gutenberg (если не используете)
wp_dequeue_style( 'wp-block-library' );
wp_dequeue_style( 'wp-block-library-theme' );
// Убираем emoji-скрипты WordPress
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
// Убираем jQuery Migrate (если не нужен)
if ( ! is_admin() ) {
wp_deregister_script( 'jquery' );
wp_register_script( 'jquery', includes_url( '/js/jquery/jquery.min.js' ), array(), null, true );
}
// Убираем стили Contact Form 7 со страниц без формы
if ( ! is_page( array( 'contacts', 'feedback' ) ) ) {
wp_dequeue_style( 'contact-form-7' );
wp_dequeue_script( 'contact-form-7' );
}
}
// Убираем глобальные стили (Global Styles inline CSS)
remove_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles' );
remove_action( 'wp_body_open', 'wp_global_styles_render_svg_filters' );
Приоритет 100 важен — он гарантирует, что наша функция выполнится после того, как плагины подключат свои ресурсы. Стандартный приоритет 10 может не сработать.
CDN fallback: загрузка с CDN с локальным запасным вариантом
Популярные библиотеки выгодно загружать с CDN — они могут быть закешированы браузером. Но CDN может быть недоступен, поэтому нужен fallback:
add_action( 'wp_enqueue_scripts', 'mytheme_cdn_with_fallback' );
function mytheme_cdn_with_fallback() {
// jQuery с CDN
wp_deregister_script( 'jquery' );
wp_register_script(
'jquery',
'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js',
array(),
'3.7.1',
true
);
wp_enqueue_script( 'jquery' );
// Fallback если CDN не загрузился
wp_add_inline_script( 'jquery',
'window.jQuery || document.write(\'<script src="' .
esc_url( includes_url( '/js/jquery/jquery.min.js' ) ) .
'"><\/script>\')'
);
}
Атрибуты async и defer
Начиная с WordPress 6.3 появилась встроенная поддержка стратегий загрузки скриптов через параметр $args:
// WordPress 6.3+: нативная поддержка strategy
wp_register_script(
'mytheme-analytics',
get_stylesheet_directory_uri() . '/js/analytics.js',
array(),
'1.0.0',
array(
'in_footer' => true,
'strategy' => 'defer', // или 'async'
)
);
wp_enqueue_script( 'mytheme-analytics' );
// Для старых версий WordPress — фильтр
add_filter( 'script_loader_tag', 'mytheme_add_async_defer', 10, 3 );
function mytheme_add_async_defer( $tag, $handle, $src ) {
$async_scripts = array( 'mytheme-analytics', 'mytheme-tracking' );
$defer_scripts = array( 'mytheme-comments', 'mytheme-lazy' );
if ( in_array( $handle, $async_scripts, true ) ) {
return str_replace( ' src=', ' async src=', $tag );
}
if ( in_array( $handle, $defer_scripts, true ) ) {
return str_replace( ' src=', ' defer src=', $tag );
}
return $tag;
}
Версионирование и сброс кеша
Параметр версии добавляется к URL файла как ?ver=1.2.0. При обновлении файла меняйте версию, чтобы браузер загрузил новую копию. Можно автоматизировать это через filemtime:
wp_enqueue_style(
'mytheme-main',
get_stylesheet_directory_uri() . '/css/main.css',
array(),
filemtime( get_stylesheet_directory() . '/css/main.css' ) // время изменения файла
);
Это гарантирует, что при каждом изменении файла версия обновится автоматически, и браузер загрузит свежую версию.
Частые ошибки
- Хардкод в header.php — вместо
<link rel="stylesheet">и<script>всегда используйте enqueue. Иначе плагины не смогут управлять вашими ресурсами. - Забытый wp_head / wp_footer — без вызова этих функций в шаблоне enqueue не работает. Проверьте, что они есть в header.php и footer.php.
- Неправильный хук —
wp_enqueue_scriptsдля фронтенда,admin_enqueue_scriptsдля админки. Не путайте, иначе стили попадут не туда. - Подключение в init — некоторые разработчики используют хук
initдля enqueue. Это работает, но неправильно: условные теги типаis_page()ещё недоступны на init. - Удаление jQuery без проверки — многие плагины зависят от jQuery. Перед удалением убедитесь, что ничего не сломается.
Система enqueue — один из фундаментальных механизмов WordPress. Правильное её использование не только ускорит сайт, но и обеспечит совместимость с другими плагинами и темами. Привыкайте использовать wp_enqueue_scripts для всех ресурсов — это стандарт разработки, который сэкономит время при отладке и масштабировании проекта.
