Контакты

Асинхронная загрузка JavaScript - ускоряем загрузку страниц. Async и Defer — стратегии загрузки JavaScript Применение атрибутов async и defer для тега script

Async и Defer - стратегии загрузки JavaScript


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

Для демонстрации я создам веб-сайт, состоящий из следующих внешних зависимостей. Обратите особое внимание на соответствующие размеры файлов, так как время загрузки файлов прямо пропорционально этому.

  • HTML - страница ~ 1 МБ. Содержит фактическую разметку/содержимое, чтобы показать некоторые динамические выходные данные из JavaScript.
  • Изображение - image1.png ~ 5 Мб
  • JavaScript - file1.JS ~ 3 МБ - это ядро (основной файл) javascript и зависит от синтаксического анализа HTML. Он нужен для того, чтобы показать некоторые динамическое содержимое или смонтировать react/angular компонент на странице.
  • JavaScript - file2.js ~ 460B - небольшой, независимый файл javascript, который взаимодействует с dom.
  • JavaScript - file3.js ~ 1.5 MB - это вторичный файл js и зависит от file1.js, чтобы выполнить некоторый, обладающий более низким приоритетом код. Этот код не требуется сразу для рендеринга страницы и взаимодействия с пользователем; он показывает иконки социальных сетей, комментарии, онлайн помощь, запуск некоторых задач аналитики и т.д.
Теперь пришло время проанализировать различные подходы

Подход-1 [скрипты в разделе head]

В первом случае мы загрузим все теги scripts в раздел head нашего HTML. Ниже приведен скриншот анализа сетевой вкладки chrome страницы, к готовой для взаимодействия с пользователем.

Плюсы:

Последовательность выполнения кода различных JS-файлов будет сохранена в том порядке, в котором файлы были включены в HTML. В текущем примере, даже если file2 и file3 были загружены до file1, порядок выполнения будет правильным.

Минусы:

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

Ни один из вышеперечисленных скриптов не сможет получить доступ / манипулировать HTML-страницей, так как DOM еще не готов. Одним из возможных решений для обработки этой проблемы является прослушивание события DOMContentLoaded, а затем выполнение кода после этого. DOMContentLoadedСобытие срабатывает, когда исходный HTML-документ был полностью загружен и проанализирован, не дожидаясь завершения загрузки таблиц стилей, изображений.

Подход-2 [скрипты в конце]

Чтобы преодолеть 2 проблемы, с которыми мы сталкиваемся в первом подходе, давайте загрузим все 3 скрипта в нижней части тега body.

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

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

Минусы:

Нет прироста производительности как такового.

Подход-3 [использование атрибута Async]

HTML5 представил async атрибут script, который помогает в загрузке соответствующих файлов скрипта параллельно на другой поток, не влияя на синтаксический анализ HTML.

Тем не менее, соответствующий сценарий будет проанализирован и выполнен, как только он завершит загрузку, независимо от того, завершен ли или нет синтаксический анализ HTML, и будет иметь ссылку на элемент DOM до этой конкретной точки.

Здесь вы можете четко увидеть, что file2.js был загружен до HTML файла. Однако, в то время как браузер загружает file2, он не приостановил синтаксический анализ HTML и из - за этого, ко времени его выполнения-он имел ссылку на заполнитель html, чтобы ввести динамическое содержимое.

Плюсы: Поскольку скрипты загружаются в другом потоке, синтаксический анализ HTML не будет приостановлен, и пользователь сможет видеть непосредственный контент вместо белого пустого экрана. Основной прирост производительности, т. е. DOMContentLoaded время уменьшилось с 47.68 секунд до всего 21.12 секунд и составляет ~ 55% прироста.

Минусы:

Последовательность выполнения JS не сохраняется. Он выполняется в соответствующем порядке загрузки, а не включенной последовательности скриптов внутри HTML. Поддержка браузера - не поддерживается на старых веб-браузерах, т. е. IE 9 и ниже.

Что произойдет, если JS загружается до того, как DOM элемент будет доступен?Будет выброшена выброшена ошибка.

Примечание: размещение скриптов с атрибутом async в нижней части раздела body будет бесполезным и эквивалентным подходу-2.

Подход-4 [использование атрибута Defer]

Defer атрибут заставит скрипт выполниться только после того как парсинг HTML был завершен. Один очень важный момент, который следует учитывать здесь, заключается в том, что Chrome еще не поддерживает отсрочку и не оказывает влияния на продолжительность DOMContentLoaded. Однако он выполняет скрипты в конце синтаксического анализа HTML.

Плюсы:

Последовательность импорта скриптов сохраняется. Итак, file3.js выполняется только после завершения загрузки и выполнения file1, даже если file3 был загружен ранее.

Поддержка браузеров- он имеет лучшую поддержку браузеров по сравнению с атрибутом asynс, т. е. частично поддерживается в IE v6-9

Скрипты могут получить доступ к DOM, так как он выполняется только после разбора полного HTML.

Минусы:

Первоначально я думал, что отсрочка будет лучшим выбором, чем асинхронность, но позже обнаружил, что Chrome еще не поддерживает его [версия 71.0.3578.98] и не оказывает влияния на продолжительность DOMContentLoaded.

Тем не менее, он работает так, как ожидалось, в Firefox со значительным улучшением производительности.

Выводы

Предпочтительнее размещать теги скриптов в разделе head с атрибутом async для сторонних библиотек, которые зависят от Google Analytics, Google reCAPTCHA или чего-либо еще, что не требует DOM-доступа, поскольку соответствующие скрипты загружаются параллельно, но выполняются немедленно.

Используйте defer для всех других скриптов, загруженных в разделе head, поскольку они также будут загружаться параллельно, но будут выполняться только после завершения синтаксического анализа HTML и DOM готов к доступу/манипуляции.

Вы также можете использовать сочетание DOMContentLoaded listener внутри асинхронных скриптов для выполнения функциональности позже. Пожалуйста, оставьте свои мнения и выводы в комментариях, и я буду рад обсудить их с вами.


Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием.

Выход есть: поместить явавские строки в конец html документа (следовательно их загрузка будет происходить после прорисовки всей страницы) и только после этого содержимое блоков будет отображено в нужных местах. Это называется . Все серьезные проекты сегодня стараются как можно быстрее перейти на новую технологию загрузки. Тем более, что это абсолютно легко.

Есть несколько подходов. Начну по порядку.

script src= type= "text/javascript" >

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

Стандарт HTML5 поддерживает возможность асинхронной загрузки скриптов, что может значительно ускорить общее время получения страницы. Просто добавьте async или defer .

< script async src= "http://www.site.ru/script.js" type= "text/javascript" >

< script defer src= "http://www.site.ru/script.js" type= "text/javascript" >

Чем же отличаются атрибуты async и defer

В обоих случаях мы получаем асинхронную загрузку скриптов. Разница заключается только в моменте, когда скрипт начинает выполнятся. Скрипт с атрибутом async выполнится при первой же возможности после его полной загрузки, но до загрузки объекта window. В случае использования атрибута defer – скрипт не нарушит порядок своего выполнения по отношению к остальным скриптам и его выполнение произойдет после полной загрузки и парсинга страницы, но до события DOMContentLoaded объекта document.

К сожалению, этот механизм на сегодняшний день не работает во всех браузерах (особенно это касается IE). Также не будет работать, если в файле script.js есть строки document.write .

Асинхронная загрузка javascript скриптом от Google

Как известно всем мастерам, Google уделяет особое внимание скорости загрузки сайтов, и понижает медленные в поисковой выдаче. Что бы помочь, Гугл разработал специальный скрипт, при помощи которого можно сделать асинхронную загрузку javascript.

Чтобы использовать, просто заменяем

на

И подключаем файл скрипта extsrc.js

Получится так:

< script src= "http://extsrcjs.googlecode.com/svn/trunk/extsrc.js" > < script extsrc= "...." >

К сожалению, этот способ тоже не подойдет к файлам с document.write

Лучшая рабочая асинхронная загрузка javascript

Универсальный способ для всех браузеров. Работает даже с document.write

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

< div id= "script_block" class = "script_block" >

В самом конце страницы перед вставляем скрипт для асинхронной загрузки файлов:

< div id= "script_ad" class = "script_ad" style= "display:none;" > Здесь любой файл или скрипт, который нужно загрузить. < script type= "text/javascript" > // переместить его в реальную позицию отображения document. getElementById("script_block" ) . appendChild(document. getElementById("script_ad" ) ) ; // показать document. getElementById("script_ad" ) . style. display = "block" ;

В самых старых версиях IE (6 и ниже) асинхронная загрузка к сожалению не работает, но таких пользователей уже практически нет. Все остальные браузеры и сервисы успешно пользуются современной ускоренной загрузкой web-страниц.

Синхронные скрипты - это плохо, они заставляют браузер блокировать построение DOM дерева: сначала нужно получить скрипт, выполнить его, а только после этого продолжать обработку оставшейся части страницы. Это, конечно, для вас не новость, и причина, по которой мы, как евангелисты, продвигали использование асинхронных сценариев. Вот простой пример:

var script = document .createElement("script" ); script.src = "http://somehost.com/awesome-widget.js" ; document .getElementsByTagName("head" ).appendChild(script);

В чем разница? В «плохом» варианте мы блокируем построение DOM дерева, ждем пока скрипт загрузится, выполнится, а только потом продолжаем обрабатывать остальную часть документа. Во втором примере мы сразу начинаем выполнять сценарий, который создает элемент script , указывающий на внешний ресурс, добавляем его в документ, и продолжаем обработку DOM. Различие тонкое, но очень важное: динамически-создаваемые скрипты не блокирующие.

Так, это же здорово, верно? Динамически-создаваемые скрипты - это вещь! Не так быстро.

Встраиваемый JavaScript имеет небольшой, но важный (и часто упускаемый из виду) подводный камень: CSSOM блокирует его перед выполнением. Почему? Браузер не знает, что именно такой скрипт планирует сделать, а поскольку JavaScript может манипулировать CSS свойствами, он блокируется и ждет, пока анализируется CSS и строится CSSOM. Лучше один раз увидеть, чем сто раз услышать, рассмотрим следующий пример:

Погодите секундочку, что происходит? Оба сценария будут загружены заранее и выполнены через ~2.7 секунды после загрузки страницы. Обратите внимание, что скрипты будут по прежнему выполняться только после того, как будет доступен CSS (~2.7 секунды), но поскольку скрипты уже загружены в тот момент когда становится доступен CSSOM, мы можем выполнять их сразу, экономя более секунды времени обработки. Мы всё делали неправильно?

Прежде чем мы ответим на этот вопрос, давайте рассмотрим еще один пример, на этот раз с атрибутом «async» :

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

Атрибут async в теге script реализует два важных свойства: он говорит браузеру, чтобы тот не блокировал построение DOM и не блокировал выполнение скриптов перед построением CSSOM. В результате, скрипты выполняются сразу после того, как загрузятся (~1.6 секунды), не дожидаясь CSSOM. Краткий список результатов:

Так почему мы до сих пор предлагали использовать шаблон, использующий динамически создаваемые скрипты?

Оригинальная статья: Script-injected "async scripts" considered harmful Статью вычитывали: visitorFM , zenwalker , FMRobot

Подключаемые скрипты (JavaScript) блокирует загрузку HTML кода. Когда браузер (парсер) доходит до тега он останавливается, чтобы загрузить контент файла и выполнить его код, и только после этого продолжает парсинг HTML.

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

У элемента есть два атрибута, async и defer , которые могут дать нам больше контроля над тем, как и когда файл загружаются и выполняются.

Обычное выполнение ... ... .... Атрибут async
Атрибут defer
Где и что использовать?

Зависит от ситуации. Рассмотрим несколько вопросов на эту тему.

Где находится элемент ?

Если файл JavaScript находится непосредственно перед закрывающим тегом , использовать async или defer не имеет смысла поскольку к этому моменту парсер уже проанализировал весь HTML код.

Является ли скрипт самодостаточным?

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

Нужен ли полностью загруженный DOM для работы скрипта?

Если это необходимо, то использование async уместно только в случае если скрипт рассчитан на асинхронную загрузку - т.е. он ждет события пока загрузиться DOM и только потом начинает свою работу.

Или можно использовать атрибут defer . В этом случае размещать вызов script можно в любом месте HTML.

Маленький ли скрипт?

Если скрипт относительно мал и от него зависят или он зависит от других скриптов, то его можно встроить прямо в HTML (подключить inline).

Поддержка браузерами 97% Добавление атрибутов defer или async в WordPress

Штатных способов сделать это нет, поэтому будем пользоваться хуком script_loader_tag :

Add_action("wp_enqueue_scripts", "my_scripts_method"); function my_scripts_method(){ // подключаем скрипт wp_enqueue_script("my-script", get_template_directory_uri() . "/js/my-script.js"); // Добавляем атрибут defer к скрипту с id `my-script` add_filter("script_loader_tag", "change_my_script", 10, 3); function change_my_script($tag, $handle, $src){ if("my-script" === $handle){ // return str_replace(" src", " async src", $tag); return str_replace(" src", " defer src", $tag); } return $tag; } }

Понравилась статья? Поделитесь ей