Асинхронная загрузка скриптов в wordpress. Асинхронная загрузка JavaScript. Надежный вариант асинхронной загрузки

С ростом скорости интернет соединения и увеличении мощности не только десктопных, но и мобильных устройств веб страницы стают более "тяжелыми". Количество и размер подключаемых файлов растет: JavaScript файлы, css файлы, изображения, виджеты сторонних сайтов, iframe. На данный момент специфика работы браузеров такая, что при загрузке js файла блокируется отривсовка до того момента, пока скрипт не выполниться. Современные браузеры в фоновом режиме будут парсить документ и скачивать скрипты и стили, но отрисовка будет заблокирована. Сравнение сетевых параметров для различных браузеров можно посмотреть на browserscope.org . Мы не можем устранить блокировку полностью, но можем оптимизировать серверную и клиентскую часть приложения, что бы блокировка отрисовки занимала наименьший промежуток времени.

Решения для серверной части:
- Уменьшить размер передаваемых файлов
- Использовать CDN
- Вынести статические файлы на отдельный домен или под домен, таким образом увеличить количество одновременных соединений браузера.
- Включить сжатие передаваемых файлов(gzip)

Решения для клиентской части:
- Уменьшить количество запросов.
- Кэшировать файлы на стороне клиента с помощью заголовков Expires и Etags.
- Использовать общедоступные CDN(Google CDN, Yandex CDN). Таким образом, существует вероятность, что файл с общедоступного CDN уже будет храниться в кеше браузера.

Одним из способов оптимизации скорости загрузки сайта является асинхронная загрузка файлов, которая не блокирует отрисовку.

Скрипт асинхронной загрузки JavaScript:

(function() { var s = document.createElement("script"); s.type = "text/javascript"; s.async = true; s.src = "URL файла"; document.getElementsByTagName("head").appendChild(script); })();

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

If (window.addEventListener) { window.addEventListener("load", async_load, false); } else if (window.attachEvent) { window.attachEvent("onload", async_load); }

Скрипт асинхронной загрузки JavaScript с учетом события onload (function() { function async_load(){ var s = document.createElement("script"); s.type = "text/javascript"; s.async = true; s.src = "URL файла"; document.getElementsByTagName("head").appendChild(script); } if (window.addEventListener) { window.addEventListener("load", async_load, false); } else if (window.attachEvent) { window.attachEvent("onload", async_load); } })();

Но это единичный случай, когда требуется загрузка одного файла. Часто на практике подключается множество файлов.

Скрипт асинхронной загрузки множества подключаемых JavaScript файлов (function() { function async_load(){ [ "URL_файла_1.js", "URL_файла_2.js", "URL_файла_3.js" ].forEach(function(src) { var s = document.createElement("script"); s.type = "text/javascript"; s.async = true; s.src = src; document.getElementsByTagName("head").appendChild(script); }); } if (window.addEventListener) { window.addEventListener("load", async_load, false); } else if (window.attachEvent) { window.attachEvent("onload", async_load); } })();

Но в такой реализации есть минус - скрипты будут загружаться в произвольном порядке и соответсвенно выполнятся они будут произвольно во времени. Данный скрипт асинхронной загрузки идеально подходит, если выполнение JavaScript файлов не зависят один от другого и не зависит от DOM. В обратном случае его использование может привести к ошибкам на странице или непредвиденному результату выполнения. Для последовательного выполнения, но асинхронной загрузки, нужно указать async=false, тогда файлы будут скачиваться в произвольном порядке, но выполняться по очереди.

HTML 5. Асинхронная загрузка JavaScript

Стандарт HTML 5 поддерживает асинхронную загрузку JavaScript. Это можно сделать путем добавления ключевого слова async или defer. Например:

Скрипт, который подключен с атрибутом defer выполнится не нарушая порядок выполнения по отношению к остальным скриптам и его выполнение произойдет после полной загрузки и парсинга страницы, но перед тем, как вызовется DOMContentLoaded.

Скрипт, который подключен с атрибутом async выполнится при первой возможности после полной загрузки, но при этом не ожидает окончания парсинга документа и до загрузки объекта window. Браузеры не гарантируют выполнение скриптов в том же порядке в котором они подключены.

Библиотеки для асинхронной загрузки JavaScript

RequireJS - модуль загрузки JavaScript. Оптимизирован под браузеры, но он может использоваться в других средах, таких как Node, Rhino.

Require(["script"], function(script) { console.log("start after load script.js"); });

extsrc.js - библиотека, которая запускает скрипты на выполнение после того, как страница загрузится и отобразится пользователю. Работает корректно с document.write.

yepnope.js - позволяет совершать асинхронную загрузку JavaScript и CSS файлов.

Yepnope([ "script.js", "style.css" ]);

Простой способ загрузки JavaScript скриптов

Оказывается, что на практике добиться оптимальной кросс браузерной загрузки JavaScript скриптов, которые не блокируют отображение сложно, а порой невозможно. Наиболее оптимальный способом является добавление в конец документа перед закрывающимся тегом body. Из за ограничения разных браузеров и самого HTML такой вариант загрузки, который не блокирует отображение, можно считать наиболее простой.

Если Вы когда-нибудь задумывались о проблемах скорости загрузки страниц Вашего сайта, то наверняка уже знакомы с таким сервисом от Гугла как PageSpeed Insights. А если так, то наверняка знакомы с проблемой когда основное содержимое страницы загружается после того как будут загружены определённые скрипты и стили.

Гугл в свою очередь пытается ткнуть носом в это безобразие и предлагает свое решение проблемы - Удалите код JavaScript и CSS, блокирующий отображение верхней части страницы.

Что с этим делать? Конечно, можно пойти наиболее трудоемким, но действенным способом – перебрать весь движок Джумлы, внести изменения в расширения и так далее. Способ этот действительно хорош, но если честно настолько трудоемкий что связываться с ним нет никакого желания, а для новичков такой метод вовсе не выход.

Наиболее простой и не менее верный способ это воспользоваться определенными расширениями, которые сделают всю (или почти всю) работу за нас. Одно из таких расширений это плагин Javascript Async & Defer , с помощью которого можно запускать скрипты асинхронно либо отложить их запуск, но при условии, что это не навредит работе сайта.

Для начала необходимо скачать плагин Javascript Async & Defer, а следом установить. Но одной установки будет недостаточно, главное правильно настроить данное расширение. Как это сделать я сейчас постараюсь объяснить.

Настройка плагина Javascript Async and Defer

После установки плагина можно приступать к его настройке, ведь сам себя он настроить не сможет. Для этого в панели управления переходим на страницу «Менеджер плагинов: Плагины» (Расширения -> Плагины). Находим только что установленное расширение в виде плагина «Javascript Async and Defer».

Как видите, плагин по умолчанию отключен, но как я и говорил, одного включения для правильной работы будет недостаточно. Давайте откроем плагин для редактирования и посмотрим, что мы имеем. А имеем мы ни много ни мало всего одну вкладку «Плагин» с немногочисленными настройками.

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

Для начала давайте разберемся, что и как тут настраивать, как узнать какие скрипты запускаются и как их прописать. Для начала разберем суть каждой настройки:

данный переключатель дает возможность сделать загрузку скриптов в асинхронном режиме. – отложенная загрузка указанных скриптов
  • Scripts to modify – в данное текстовое поле необходимо вводить относительный путь до скриптов, которые будем обрабатывать.
  • Debug – режим отладки, пригодится для выявления скриптов, которые запускаются перед загрузкой контента.
  • Первое, что нам необходимо узнать, это какие скрипты запускаются при загрузке той или иной страницы, для этого нужно воспользоваться режимом отладки «Debug». В этом режиме на сайте в области системных сообщений (уведомлений) будет выведен список скриптов, которые загружаются совместно со страницей.

    Но не все так просто, если честно, то этот самый список будет выведен только в том случае, если вы укажите хотя бы один скрипт для модификации в поле Scripts to modify. А как быть, если вы знать ни знаете какой скрипт там указывать? Ничего страшного для начала можно написать в данное поле что угодно, любое слово или символ, после этого включаем режим отладки, активируем плагин и сохраняем результат.

    Теперь можно перейти на сайт и посмотреть на результат.

    Самый простой вариант скопировать все те скрипты, которые выдал нам плагин и добавить в поле для модификации «Scripts to modify». Затем выбрать режим загрузки (асинхронный, отложенный, или сразу оба) сохранить изменения и проверить результат.

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

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

    HTML устроен таким образом, что веб-страница загружается последовательно строка за строкой, загружая по очереди все включенные в html-код элементы. И если один из них недоступен (например, не считывается javaScript с внешнего сайта), то дальнейшая загрузка сайта останавливается.

    Когда нечитаемый JS находится в начале страницы посетитель может вообще ничего не увидеть.

    Поэтому, когда вы на своем сайте используете JavaScript со сторонних сайтов, например для показа рекламы, то крайне желательно производить их загрузку асинхронно. В этом случае сторонний JS не будет задерживать загрузку и показ вашего сайта.

    К сожалению, не все рекламные сети предоставляют возможность асинхронной загрузки скриптов. Поэтому в этой статье я расскажу как исправить синхронный код загрузки на асинхронный. Если владелец это скрипта не предоставляет такой возможности.

    Стандартная синхронная загрузка JS

    Обычно вызов скрипта с внешнего сервера выглядит так:

    Асинхронная загрузка скрипта как это делает Google/Adsense

    Идею я подсмотрел у Google/Adsense. Чтобы скрипт загружался асинхронно от остального HTML кода необходимо в код вызова добавить async .

    И теперь чтобы код загружался асинхронно наш вызов скрипта с внешнего сервера должен выглядеть так:

    Как видите все просто. Правда работать это будет только в браузерах которые поддерживают стандарт HTML5. На момент написания этой статьи таких браузеров абсолютное большинство.

    Предложенный вариант не является универсальным на 100%. Многие скрипты после внесения указанных изменений просто перестают работать. По отзывам в сети, это способ не работает если в скрипте используется элемент document.write .

    Надежный вариант асинхронной загрузки

    Если у вас не заработал первый предложенный вариант. То воспользуйтесь следующими рекомендациями. По сути это отложенная загрузка. Так как реальный вызов скрипта происходит в самом конце HTML страницы, то есть когда все необходимое содержимое страницы уже на экране.

    В том месте страницы, где нужно отобразить результат работы JavaScript нужно создать пустой div блок:

    Затем в конце страницы перед закрывающим тегом BODY вставляем скрипт для асинхронной загрузки:

    // JavaScript который надо загрузить асинхронно // переместить его в реальную позицию отображения document.getElementById("script_block_0").appendChild(document.getElementById("script_ad_0")); // показать document.getElementById("script_ad_0").style.display = "block";

    Если рекламных блоков несколько, то для каждого из них надо все повторить, создавая уникальные индивидуальные DIV блоки. Не забывайте менять имена классов и ID блоков DIV. В моем примере достаточно сменить цифру ноль, в новом блоке ее нужно заменить на 1 и так далее.

    Этот вариант более сложный, но работает везде кроме очень древних браузеров таких как Internet Explorer 6. Который,к счастью, уже практически не встречается на компьютерах пользователей.

    Эта проблема была предварительно затронута в статье: «Как JavaScript тормозит Веб (и что с этим делать)?»

    Для начала определимся с самой проблемой. Событие window.onload используется программистами для старта их веб-приложения. Это может быть что-то довольно простое, например, выпадающее меню, а может быть и совсем сложное, как пример, запуск почтового приложения. Суть проблемы заключается в том, что событие onload срабатывает только после того, как загрузится вся страница (включая все картинки и другое бинарное содержимое). Если на странице много картинок, то можно заметить некоторую задержку между загрузкой страницы и тем моментом, когда она начнет фактически работать. На самом деле, нам нужно только узнать способ определить, когда DOM полностью загрузится, а не ждать еще и загрузку картинок.

    Mozilla впереди планеты всей

    У Mozilla (прим.: на данный момент Firefox является более актуальным, поэтому далее упоминается именно он ) есть (недокументированное) событие специально для этих целей: DOMContentLoaded . Следующий образец кода выполняет как раз то, что нам нужно в Mozilla-подобных браузерах (а также в Opera 9):

    // для Firefox if (document.addEventListener) { document.addEventListener("DOMContentLoaded", init, false); } А Internet Explorer?

    IE поддерживает замечательный атрибут для тега <script> : defer . Присутствие этого атрибута указывает IE, что загрузку скрипта нужно отложить до тех пор, пока не загрузится DOM. Однако, это работает только для внешних скриптов. Следует также заметить, что этот атрибут нельзя выставлять, используя другой скрипт. Это означает, что нельзя создать с этим атрибутом, используя DOM-методы, — атрибут будет просто проигнорирован.

    Используя этот удобный атрибут, можно создать мини-скрипт, который и будет вызывать наш обработчик onload:

    Условные комментарии

    Есть некоторая проблема с этим подходом. Другие браузеры проигнорируют атрибут defer и загрузят этот скрипт сразу же. Существует несколько способов, как можно с этим побороться. Моим любимым методом является использование условных комментариев (conditional comments ), чтобы скрыть «отложенный» скрипт:

    IE также поддерживает условную компиляцию (conditional compilation ). Следующий код будет JavaScript-эквивалентом для заявленного выше HTML-кода:

    // для Internet Explorer /*@cc_on @*/ /*@if (@_win32) document.write(""); /*@end @*/ Все так просто?

    И конечно же, нам нужно обеспечить поддержку для остальных браузеров. У нас есть только один выход — стандартное событие window.onload:

    // для остальных браузеров window.onload = init; Двойное выполнение

    Остается одна маленькая неприятность (кто сказал, что будет легко?). Поскольку мы устанавливаем событие onload для всех (оставшихся) браузеров, то init сработает дважды в IE и Firefox. Чтобы это обойти, нам нужно сообщить функции, что она должна выполняться только один раз. Итак, наш метод init будет выглядеть примерно так:

    function init() { // выходим, если функция уже выполнялась if (arguments.callee.done) return; // устанавливаем флаг, чтобы функция не исполнялась дважды arguments.callee.done = true; // что-нибудь делаем };

    Прим.: лично мне кажется, что выставление глобальной переменной READY в данном случае более логично, ведь знать, что страница начала функционировать может потребовать не только одной функции init .

    Избавляемся от внешнего файла

    У описанного решения существует пара минусов:

    • Для IE нам требуется внешний JavaScript-файл
    • Не поддерживается Safari (Opera 9 поддерживает DOMContentLoaded)

    Большое спасибо Matthias Miller , теперь у нас есть решение и для Internet Explorer, которое не зависит от внешних файлов:

    // для Internet Explorer (используем условную компиляцию) /*@cc_on @*/ /*@if (@_win32) document.write(" "); var script = document.getElementById("__ie_onload"); script.onreadystatechange = function() { if (this.readyState == "complete") { init(); // вызываем обработчик для onload } }; /*@end @*/ И Safari тоже!

    Я позже почерпнул из листа рассылки jQuery , что, благодаря создателю jQuery, John Resig , существует решение и для Safari!

    if (/WebKit/i.test(navigator.userAgent)) { // условие для Safari var _timer = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { clearInterval(_timer); init(); // вызываем обработчик для onload } }, 10); }

    По всей видимости, jQuery — это первая библиотека, которая имеет универсальное решение для заявленной проблемы.

    Полное решение // Dean Edwards/Matthias Miller/John Resig function init() { // выходим, если функция уже выполнялась if (arguments.callee.done) return; // устанавливаем флаг, чтобы функция не исполнялась дважды arguments.callee.done = true; // что-нибудь делаем }; /* для Mozilla/Firefox/Opera 9 */ if (document.addEventListener) { document.addEventListener("DOMContentLoaded", init, false); } /* для Internet Explorer */ /*@cc_on @*/ /*@if (@_win32) document.write(""); var script = document.getElementById("__ie_onload"); script.onreadystatechange = function() { if (this.readyState == "complete") { init(); // вызываем обработчик для onload } }; /*@end @*/ /* для Safari */ if (/WebKit/i.test(navigator.userAgent)) { // условие для Safari var _timer = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { clearInterval(_timer); init(); // вызываем обработчик для onload } }, 10); } /* для остальных браузеров */ window.onload = init;

    25.12.2012 job 18355

    Поговорим немножко об оптимизации загрузки страницы в Joomla. На сегодня это будет подключение JavaScript. Во первых обращу внимание верстальщиков, на правильность построения шаблона. А потом рассмотрим встроенную в движок возможность использовать асинхронную загрузку внешних скриптов. Разглагольствовать для чего нужна оптимизация не буду.

    Построение шаблона

    Как правило верстальщики на Joomla не обращают внимание на порядок загрузки стилей и скриптов, а это важный аспект оптимизации для распараллеливания загрузки. CSS файлы следует всегда включать перед файлами JavaScript.

    Если в Вашем шаблоне загрузка css и javascript выглядит следующим образом:

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

    А вот как правильно включать файлы в рендеринг:

    Не обращаем внимание на валидность doctype и html, писал сокращенно для примера.

    Асинхронная загрузка

    А вот тут я бы больше заострил внимание и дочитал статью до конца! Часто заказчик на оптимизацию требует реализовывать асинхронную загрузку javascript. Как ни странно разработчики Joomla предусмотрели и такую возможность, а вот разработчики сторонних компонентом, модулей и плагинов забывают про данный функционал, видимо поэтому Jooml"у часто и критикуют за низкую оптимизацию, с чем бы я и поспорил.

    Как рассматривалось ранее в рендеринге документа существует функция addScript(); . Но видимо некоторые и не подозревают что она имеет четыре переменные - $url , $type , $defer , $async .

    $this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js"); $this->addScript($this->baseurl."/templates/beez_20/js/script.js");

    Но в нашем случае нам необходимо загрузку скриптов браузером поставить в очередь, для этого необходимо использовать атрибут async и/или defer . Оба отличаются лишь тем что defer сохраняет порядок выполнения скриптов. Defer нужен в том случае если script.js используется фреймворк jQuery, который предотвратит его загрузку раньше самой библиотеки, ну и если необходимо сохранить некую последовательность.

    И так что бы включить асинхронную загрузку необходимо использовать все переменные

    $this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js", "text/javascript"); // или $this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js", "text/javascript", false, false); // Выводит $this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js", "text/javascript", true, false); // Выводит $this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js", "text/javascript", false, true); // Выводит

    И еще один маленький моментик. В принципе данные атрибуты это элементы HTML5, следовательно нет смысла использовать вид async="async" и defer="defer", достаточно async и defer соответственно.

    Внесем коррективы в библиотеку joomla. Открываем сайт.ру/libraries/joomla/document/html/renderer/head.php

    Ищем (примерно строка 150):

    // Generate script file links foreach ($document->_scripts as $strSrc => " . $lnEnd; }

    заменяем на:

    // Generate script file links foreach ($document->_scripts as $strSrc => $strAttr) { $buffer .= $tab . "" . $lnEnd; }

    Вот и вся соль. Все манипуляции мной проводились в Joomla 2.5, как это будет работать в 1.5 хз, не рассматривал, да и пора уже забыть про нее))). Да и вышеописанная техника совсем не панацея, к каждому нужен индивидуальных подход.

    Спасибо за внимание!