- Скрипты: async, defer
- defer
- async
- Динамически загружаемые скрипты
- Итого
- Атрибут async
- Синтаксис
- Значения
- Значение по умолчанию
- Типы тегов
- Руководство: как увеличить скорость загрузки страницы со скриптами с помощью defer и async
- Расположение имеет значение
- async и defer
- Производительность
- Блокировка синтаксического анализа
- Блокировка рендеринга
- domInteractive
- Порядок выполнения
- Как ускорить загрузку страницы
Скрипты: async, defer
В современных сайтах скрипты обычно «тяжелее», чем HTML: они весят больше, дольше обрабатываются.
Когда браузер загружает HTML и доходит до тега , он не может продолжать строить DOM. Он должен сначала выполнить скрипт. То же самое происходит и с внешними скриптами : браузер должен подождать, пока загрузится скрипт, выполнить его, и только затем обработать остальную страницу.
Это ведёт к двум важным проблемам:
- Скрипты не видят DOM-элементы ниже себя, поэтому к ним нельзя добавить обработчики и т.д.
- Если вверху страницы объёмный скрипт, он «блокирует» страницу. Пользователи не видят содержимое страницы, пока он не загрузится и не запустится:
. содержимое перед скриптом.
. содержимое после скрипта.
Конечно, есть пути, как это обойти. Например, мы можем поместить скрипт внизу страницы. Тогда он сможет видеть элементы над ним и не будет препятствовать отображению содержимого страницы:
. всё содержимое над скриптом.
Но это решение далеко от идеального. Например, браузер замечает скрипт (и может начать загружать его) только после того, как он полностью загрузил HTML-документ. В случае с длинными HTML-страницами это может создать заметную задержку.
Такие вещи незаметны людям, у кого очень быстрое соединение, но много кто в мире имеет медленное подключение к интернету или использует не такой хороший мобильный интернет.
К счастью, есть два атрибута тега , которые решают нашу проблему: defer и async .
defer
Атрибут defer сообщает браузеру, что он должен продолжать обрабатывать страницу и загружать скрипт в фоновом режиме, а затем запустить этот скрипт, когда DOM дерево будет полностью построено.
Вот тот же пример, что и выше, но с defer :
. содержимое перед скриптом.
. содержимое после скрипта.
- Скрипты с defer никогда не блокируют страницу.
- Скрипты с defer всегда выполняются, когда дерево DOM готово, но до события DOMContentLoaded .
Следующий пример это показывает:
. содержимое до скрипта.
// (2) . содержимое после скрипта.
- Содержимое страницы отобразится мгновенно.
- Событие DOMContentLoaded подождёт отложенный скрипт. Оно будет сгенерировано, только когда скрипт (2) будет загружен и выполнен.
Отложенные с помощью defer скрипты сохраняют порядок относительно друг друга, как и обычные скрипты.
Поэтому, если сначала загружается большой скрипт, а затем меньшего размера, то последний будет ждать.
Браузеры сканируют страницу на предмет скриптов и загружают их параллельно в целях увеличения производительности. Поэтому и в примере выше оба скрипта скачиваются параллельно. small.js скорее всего загрузится первым.
Но спецификация требует последовательного выполнения скриптов согласно порядку в документе, поэтому он подождёт выполнения long.js .
Атрибут defer будет проигнорирован, если в теге нет src .
async
Атрибут async означает, что скрипт абсолютно независим:
- Страница не ждёт асинхронных скриптов, содержимое обрабатывается и отображается.
- Событие DOMContentLoaded и асинхронные скрипты не ждут друг друга:
- DOMContentLoaded может произойти как до асинхронного скрипта (если асинхронный скрипт завершит загрузку после того, как страница будет готова),
- …так и после асинхронного скрипта (если он короткий или уже содержится в HTTP-кеше)
Так что если у нас есть несколько скриптов с async , они могут выполняться в любом порядке. То, что первое загрузится – запустится в первую очередь:
. содержимое перед скриптами.
. содержимое после скриптов.
- Содержимое страницы отображается сразу же : async его не блокирует.
- DOMContentLoaded может произойти как до, так и после async , никаких гарантий нет.
- Асинхронные скрипты не ждут друг друга. Меньший скрипт small.js идёт вторым, но скорее всего загрузится раньше long.js , поэтому и запустится первым. То есть, скрипты выполняются в порядке загрузки.
Асинхронные скрипты очень полезны для добавления на страницу сторонних скриптов: счётчиков, рекламы и т.д. Они не зависят от наших скриптов, и мы тоже не должны ждать их:
Динамически загружаемые скрипты
Мы можем также добавить скрипт и динамически, с помощью JavaScript:
let script = document.createElement('script'); script.src = "/article/script-async-defer/long.js"; document.body.append(script); // (*)
Скрипт начнёт загружаться, как только он будет добавлен в документ (*) .
Динамически загружаемые скрипты по умолчанию ведут себя как «async».
- Они никого не ждут, и их никто не ждёт.
- Скрипт, который загружается первым – запускается первым (в порядке загрузки).
Мы можем изменить относительный порядок скриптов с «первый загрузился – первый выполнился» на порядок, в котором они идут в документе (как в обычных скриптах) с помощью явной установки свойства async в false :
let script = document.createElement('script'); script.src = "/article/script-async-defer/long.js"; script.async = false; document.body.append(script);
Например, здесь мы добавляем два скрипта. Без script.async=false они запускались бы в порядке загрузки ( small.js скорее всего запустился бы раньше). Но с этим флагом порядок будет как в документе:
function loadScript(src) < let script = document.createElement('script'); script.src = src; script.async = false; document.body.append(script); >// long.js запускается первым, так как async=false loadScript("/article/script-async-defer/long.js"); loadScript("/article/script-async-defer/small.js");
Итого
У async и defer есть кое-что общее: они не блокируют отрисовку страницы. Так что пользователь может просмотреть содержимое страницы и ознакомиться с ней сразу же.
Но есть и значимые различия:
Порядок DOMContentLoaded async Порядок загрузки (кто загрузится первым, тот и сработает). Не имеет значения. Может загрузиться и выполниться до того, как страница полностью загрузится. Такое случается, если скрипты маленькие или хранятся в кеше, а документ достаточно большой. defer Порядок документа (как расположены в документе). Выполняется после того, как документ загружен и обработан (ждёт), непосредственно перед DOMContentLoaded . Пожалуйста, помните, что когда вы используете defer , страница видна до того, как скрипт загрузится.
Пользователь может знакомиться с содержимым страницы, читать её, но графические компоненты пока отключены.
Поэтому обязательно должна быть индикация загрузки, нерабочие кнопки – отключены с помощью CSS или другим образом. Чтобы пользователь явно видел, что уже готово, а что пока нет.
На практике defer используется для скриптов, которым требуется доступ ко всему DOM и/или важен их относительный порядок выполнения.
А async хорош для независимых скриптов, например счётчиков и рекламы, относительный порядок выполнения которых не играет роли.
Атрибут async
При наличии атрибута async браузер при возможности запускает скрипт асинхронно. Это означает, что указанный в атрибуте src файл будет выполняться без ожидания загрузки и отображения веб-страницы. В то же время и страница не ожидает результата выполнения скрипта, а продолжает загружаться как обычно.
Синтаксис
Значения
Значение по умолчанию
По умолчанию этот атрибут выключен.
В данном примере загружается содержимое страницы и одновременно выполняется скрипт, который выводит сообщение через функцию alert() . Без атрибута async содержимое страницы не отображается, пока мы не закроем окно с сообщением.
Не выкладывайте свой код напрямую в комментариях, он отображается некорректно. Воспользуйтесь сервисом cssdeck.com или jsfiddle.net, сохраните код и в комментариях дайте на него ссылку. Так и результат сразу увидят.
Типы тегов
HTML5
Блочные элементы
Строчные элементы
Универсальные элементы
Нестандартные теги
Осуждаемые теги
Видео
Документ
Звук
Изображения
Объекты
Скрипты
Списки
Ссылки
Таблицы
Текст
Форматирование
Формы
Фреймы
Руководство: как увеличить скорость загрузки страницы со скриптами с помощью defer и async
JS-скрипты, неудачно расположенные в HTML-коде, могут значительно снизить скорость загрузки страницы. Разберемся, как повысить скорость загрузки в старых версиях браузеров и как правильно использовать async и defer , которые поддерживаются в новых версиях.
Это адаптированный перевод статьи Efficiently load JavaScript with defer and async из блога проекта flaviocopes. Повествование ведется от лица автора оригинала.
Расположение имеет значение
Стандартный способ встраивания скрипта в HTML-код страницы выглядит так:
Каждый раз, когда встретится такая или похожая строка, будет выполнен запрос на получение данных файла, а парсер продолжит свою работу после выполнения скрипта.
Классический подход к обучению HTML предполагает, что теги скрипта должны находиться в :
html> head> title>Titletitle> script src="script.js">script> head> body> . body> html>
Однако такой подход приводит к задержкам при загрузке страницы. Когда анализатор доходит до строки со скриптом, он на время останавливается для его извлечения и выполнения, и только после этого переходит к разбору .
Распространенное решение проблемы — перенос скрипта в нижнюю часть страницы, перед закрывающим тегом . В этом случае скрипт выполняется после того, как вся страница уже проанализирована до тега.
Это лучшее решение по ускорению загрузки страницы для старых браузеров, которые не поддерживают атрибуты async и defer . О последних поговорим отдельно.
async и defer
Прежде чем начать, стоит уточнить, что использование обоих атрибутов накладывает некоторые ограничения, а приведенное ниже руководство по времени загрузки — не исчерпывающее.
Синтаксически и async и defer — булевые атрибуты, которые используются следующим образом:
Если в коде есть оба атрибута, async имеет приоритет и выполняется в первую очередь в современных версиях браузеров. В старых версиях, напротив, приоритет будет отдан defer .
Проверить совместимость атрибутов с разными версиями браузеров можно по этим таблицам: раз и два
Важно отметить, что оба атрибута стоит использовать только в верхней части страницы (в ): перенос в делает их совершенно бесполезными.
Производительность
Если async и defer отсутствуют в
Синтаксический анализатор прекращает работу до тех пор, пока скрипт не будет выполнен. Как только этот процесс завершится, анализ продолжится.
Читайте также: Как сохранять фокус на протяжении всего обучения: советы от Хекслета
Если async и defer отсутствуют в
Парсинг выполняется без пауз: сразу по его завершению загружается и выполняется скрипт. Синтаксический анализ выполняется еще до загрузки скрипта, поэтому страница загружается быстрее, чем в предыдущем случае.
Если async находится в
Сценарий загружается асинхронно, а синтаксический анализатор приостанавливает работу на время его выполнения.
Если defer находится в
Скрипт извлекается асинхронно и выполняется только после завершения анализа HTML.
Парсинг проходит с той же скоростью, как если бы скрипт находился в конце тега body , но в целом выполнение скрипта завершается намного раньше, поскольку он загружается параллельно с парсингом HTML. Таким образом этот вариант — наиболее выигрышный с точки зрения скорости загрузки страницы.
Блокировка синтаксического анализа
async приостанавливает синтаксический анализ страницы, а defer — нет.
Блокировка рендеринга
Ни async , ни defer не блокируют рендеринг — этот процесс полностью зависит от кода на странице. Поэтому важно убедиться, что сценарии запускаются после события onLoad .
domInteractive
Скрипты defer выполняются сразу после события domInteractive . Последнее, в свою очередь, происходит после загрузки, анализа и построения DOM HTML.
СSS и изображения на этом этапе еще не проанализированы и не загружены: как только это произойдет, браузер сначала выдаст событие domComplete , а затем — onLoad .
Порядок выполнения
Еще один аргумент за использование defer — скрипты, помеченные как async , выполняются в случайном порядке, тогда как скрипты с defer — в строго определенном.
Как ускорить загрузку страницы
Лучший способ — прописать скрипты в и добавить атрибут defer в тег script . Этот сценарий быстро запускает событие domInteractive :
Никогда не останавливайтесь: В программировании говорят, что нужно постоянно учиться даже для того, чтобы просто находиться на месте. Развивайтесь с нами — на Хекслете есть сотни курсов по разработке на разных языках и технологиях