Статья актуализирована для версии WP 6.5.4, менять её буду только в том случае, если код перестанет работать.
С версии WP 4.1 был добавлен отличный фильтр — script_loader_tag. Теперь нет необходимости извергать пламя из не предназначенных для того мест при добавление к скриптам атрибутов async или defer. Все делается довольно просто.
P. S. весь предложенный ниже код подключать требуется через файл functions.php, желательно, в рамках дочерней темы. Также можно вынести в собственный плагин. Прочитайте о создании собственного плагина.
Настройка асинхронной или отложенной загрузки js с помощью плагина
На данный момент лучший способ. Устанавливаем из репозитория плагин Async JavaScript: https://ru.wordpress.org/plugins/async-javascript/.
А потом просто активируем его. Отлично работает в связке с Autoptimize (от одного автора плагины).
После установки переходим в настройки плагина. Вы их найдёте либо в настройках в админке WordPress, либо на странице с плагинами.
Дальше активируем плагин, включаем Enable Async JavaScript? и Also enable Async JavaScript for logged in users? (это хорошо в том случае, если у вас на сайте есть зарегистрированные пользователи).
И выберите метод:
- Если вам нужна асинхронная загрузка js, ставьте метод Async.
- Если отложенная загрузка, то ставьте метод Defer.
Обратите внимание на раздел Quick Settings, если у вас что-то ломается на сайте, то можете выбрать настройку, включающую в себя (jQuery excluded), то бишь исключает jQuery из работы плагина, остальные файлы будут загружать асинхронно или отложено.
Чуть ниже, в разделе Scripts to Async вы можете выставить вручную, каким скриптам будет присвоен тег async.
В разделе Scripts to Defer тоже самое, но для этих скриптов будет включена отложенная загрузка.
Ну а если какие-то скрипты всё равно ломаются, то можете исключить их в разделе Script Exclusion.
Также вы можете целиком исключить ресурсы тем или плагинов из Async JavaScript.
Если у вас стоит плагин Autoptimize, то он по умолчанию присваивает всем скриптам тег defer, так что вам нужно это переназначить. Пролистайте чуть ниже и поставьте галочку рядом с “Enable Autoptimize Support to allow you to override AO’s default “defer” flag (see below):”
И обязательно выберите метод, с которым будет работать плагин Async JavaScript.
После чего нажмите кнопку Sava Settings. Готов, вы только что настроили асинхронную или отложенную загрузку скриптов.
Настройка асинхронной или отложенной загрузки js без плагинов
Соответственно, предлагаю вам рабочий вариант кода для асинхронной загрузки. Это результат небольшой переработки нижеследующих вариантов, но главное отличие в том, что «ручками» вам придётся только исключать код, который не хочет работать асинхронно. Нижеприведённый код оптимизирован под версию 6.5.4 и работает, рекомендую использовать именно его.
А теперь сам код для асинхронной загрузки скриптов:
<?php
if (!is_admin()) {
add_filter( 'script_loader_tag', function ( $tag, $handle ) {
if ( strpos( $tag, "jquery.js" ) || strpos( $tag, "jquery-migrate.min.js") || strpos ($tag, "jquery.min.js" ) ) {
return $tag;
}
return str_replace( ' src', 'async src', $tag );
}, 10, 2 );
}
Все исключения вносите в эту строку в таком же формате.
if ( strpos( $tag, "jquery.js" ) || strpos( $tag, "jquery-migrate.min.js") ) //Исключение указывайте в таком виде "strpos( $tag, "jquery-migrate.min.js")", не забудьте разделитель ||
Если решите использовать отложенную загрузку, то вот вам вариант для неё.
add_filter( 'script_loader_tag', function ( $tag, $handle ) {
if ( strpos( $tag, "jquery.js" ) || strpos( $tag, "jquery-migrate.min.js") || strpos ($tag, "jquery.min.js" ) ) {
return $tag;
}
return str_replace( ' src', 'defer src', $tag );
}, 10, 2 );
}
Я всего лишь заменил async на defer.
Ну а если предложенные варианты не помогли, то попробуйте то, что указал ниже.
В самом начале я старался использовать что-нибудь универсальное, чтобы не было проблем, например, это:
function wcs_defer_javascripts ( $url ) {
if(is_admin()) return $url;
if ( FALSE === strpos( $url, '.js' ) ) return $url;
if ( strpos( $url, 'jquery.js') ) return $url;
return "$url' defer='defer";
}
add_filter( 'clean_url', 'wcs_defer_javascripts', 11, 1 );
Для запуска асинхронной загрузки достаточно поменять строку:
return "$url' defer='defer";
на:
return "$url' async='async";
Но, такой код не всегда давал значительный прирост, да и мог порушить структуру сайта.
Я решил задачку усложнить, чтобы более точно управлять отложенной и асинхронной загрузкой в WordPress и нашёл такое решение:
function add_async_forscript($url)
{
if (strpos($url, '#asyncload')===false)
return $url;
else if (is_admin())
return str_replace('#asyncload', '', $url);
else
return str_replace('#asyncload', '', $url)."' async='async";
}
add_filter('clean_url', 'add_async_forscript', 11, 1);
Теперь к любому скрипту можно просто добавить #asyncload, чтобы присвоить атрибут async. Пример ниже:
wp_enqueue_script('myscriptass', 'assest/js/myscriptass.js#asyncload' );
Таким образом требуется подключать скрипты в файле functions.php. В кастомном плагине не рекомендуется, так что не самый лаконичный способ.
Для отложенной загрузки создавал аналогичный код с атрибутами defer. Все довольно просто. Но есть более правильное решение, о котором расскажу дальше. И появилось оно вместе с выпуском WP 4.1 и добавлением нового фильтра.
apply_filters('script_loader_tag', string $tag, string $handle, string $src);
//виновник торжества
Это и есть нужный фильтр. Просто для примера, его вставлять никуда не требуется.
Теперь переходим непосредственно к добавлению асинхронной загрузки на отдельный скрипт:
function add_async_attribute($tag, $handle) {
if ( 'my-js-handle' !== $handle )
return $tag;
return str_replace( ' src', ' async="async" src', $tag );
}
add_filter('script_loader_tag', 'add_async_attribute', 10, 2);
Если необходимо добавить отложенную загрузку, то код будет выглядеть так:
function add_defer_attribute($tag, $handle) {
if ( 'my-js-handle' !== $handle )
return $tag;
return str_replace( ' src', ' defer="defer" src', $tag );
}
add_filter('script_loader_tag', 'add_defer_attribute', 10, 2);
Для отложенной загрузки просто заменили async на defer.
Наверное, содержание вызвало вопрос. Итак, все довольно просто. Данная функция проверяет скрипт на соответствие. Если js не соответствует параметру $handle, то возвращает атрибут по умолчанию, если же соответствует, то присваивает нужный атрибут. Наверное, возник вопрос, где найти значение $handle?
В таких строках:
wp_register_script('my-js-handle', $src, $deps, $ver, $in_footer);
или:
wp_enqueue_script('my-js-handle', $src, $deps, $ver, $in_footer);
Располагаться они могут практически в любом файле, который запускает загрузку скриптов. На скриншоте показываю наглядно, что вам нужно найти.
Ничего сложного, кроме того, что на каждый скрипт пилить такую структуру трудоёмко, потому, сделаем код более массовым.
function add_defer_attribute($tag, $handle) {
// добавляем дескрипторы в массив
$scripts_to_defer = array('my-js-handle', 'another-handle'); //здесь размещаем те самые $handle
foreach($scripts_to_defer as $defer_script) {
if ($defer_script === $handle) {
return str_replace(' src', ' defer="defer" src', $tag);
}
}
return $tag;
}
add_filter ('script_loader_tag', 'add_defer_attribute', 10, 2);
Отличие кода только в том, что на соответствие проверяются все значения массива. И если соответствие найдено, то будет присвоен атрибут defer. Для асинхронной загрузки код отдельный.
function add_async_attribute($tag, $handle) {
// добавляем дескрипторы в массив
$scripts_to_async = array('my-js-handle', 'another-handle'); //здесь размещаем те самые $handle
foreach($scripts_to_async as $async_script) {
if ($async_script === $handle) {
return str_replace(' src', ' async="async" src', $tag);
}
}
return $tag;
}
add_filter ('script_loader_tag', 'add_async_attribute', 10, 2);
Чем хорош данный вариант? Можно с лёгкостью разбить часть скриптов на отложенные, другую на асинхронные, избежав конфликтов. Но для этого придётся понимать, как и в какой последовательности работают скрипты на сайте.
Надеюсь, сумел вам помочь, задача нелёгкая, но, как видите, решаемая.
Кл куда вставлять этот?
<?php
if (!is_admin()) {
add_filter( 'script_loader_tag', function ( $tag, $handle ) {
if ( strpos( $tag, "jquery.js" ) || strpos( $tag, "jquery-migrate.min.js") || strpos ($tag, "jquery.min.js" ) ) {
return $tag;
}
return str_replace( ' src', 'async src', $tag );
}, 10, 2 );
}
в файл функцион.пнп? до какого тега? до боди?
К тегу body этот код вообще не имеет никакого отношения, он работает со всеми скриптами, которые есть на сайте, кроме тех, что подгружаются с внешних источников (например, коды счётчиков), вообще, если у вас в functions.php есть теги разметки шаблонов, то это не очень хорошо, этот файл предназначен для другого. В общем, этот код можно добавить в functions.php, желательно создать дочернюю тему, иначе при обновлении темы весь код слетит, если вы не используете дочернюю тему, то можно сделать собственный плагин и вынести код туда. Разницы никакой. Но плагин будет удобнее.