Html порядок загрузки скриптов

Скрипты: async, defer

В современных сайтах скрипты обычно «тяжелее», чем HTML: они весят больше, дольше обрабатываются.

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

Это ведёт к двум важным проблемам:

  1. Скрипты не видят DOM-элементы ниже себя, поэтому к ним нельзя добавить обработчики и т.д.
  2. Если вверху страницы объёмный скрипт, он «блокирует» страницу. Пользователи не видят содержимое страницы, пока он не загрузится и не запустится:

. содержимое перед скриптом.

. содержимое после скрипта.

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

 . всё содержимое над скриптом.  

Но это решение далеко от идеального. Например, браузер замечает скрипт (и может начать загружать его) только после того, как он полностью загрузил HTML-документ. В случае с длинными HTML-страницами это может создать заметную задержку.

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

Читайте также:  Method without parameters java

К счастью, есть два атрибута тега , которые решают нашу проблему: defer и async .

defer

Атрибут defer сообщает браузеру, что он должен продолжать обрабатывать страницу и загружать скрипт в фоновом режиме, а затем запустить этот скрипт, когда DOM дерево будет полностью построено.

Вот тот же пример, что и выше, но с defer :

. содержимое перед скриптом.

. содержимое после скрипта.

  • Скрипты с defer никогда не блокируют страницу.
  • Скрипты с defer всегда выполняются, когда дерево DOM готово, но до события DOMContentLoaded .

Следующий пример это показывает:

. содержимое до скрипта.

// (2)

. содержимое после скрипта.

  1. Содержимое страницы отобразится мгновенно.
  2. Событие DOMContentLoaded подождёт отложенный скрипт. Оно будет сгенерировано, только когда скрипт (2) будет загружен и выполнен.

Отложенные с помощью defer скрипты сохраняют порядок относительно друг друга, как и обычные скрипты.

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

Браузеры сканируют страницу на предмет скриптов и загружают их параллельно в целях увеличения производительности. Поэтому и в примере выше оба скрипта скачиваются параллельно. small.js скорее всего загрузится первым.

Но спецификация требует последовательного выполнения скриптов согласно порядку в документе, поэтому он подождёт выполнения long.js .

Атрибут defer будет проигнорирован, если в теге нет src .

async

Атрибут async означает, что скрипт абсолютно независим:

  • Страница не ждёт асинхронных скриптов, содержимое обрабатывается и отображается.
  • Событие DOMContentLoaded и асинхронные скрипты не ждут друг друга:
    • DOMContentLoaded может произойти как до асинхронного скрипта (если асинхронный скрипт завершит загрузку после того, как страница будет готова),
    • …так и после асинхронного скрипта (если он короткий или уже содержится в HTTP-кеше)

    Так что если у нас есть несколько скриптов с async , они могут выполняться в любом порядке. То, что первое загрузится – запустится в первую очередь:

    . содержимое перед скриптами.

    . содержимое после скриптов.

    1. Содержимое страницы отображается сразу же : async его не блокирует.
    2. DOMContentLoaded может произойти как до, так и после async , никаких гарантий нет.
    3. Асинхронные скрипты не ждут друг друга. Меньший скрипт 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 хорош для независимых скриптов, например счётчиков и рекламы, относительный порядок выполнения которых не играет роли.

    Источник

    Порядок выполнения скриптов в HTML. Тег script и атрибуты async, defer, module, nomodule и src

    Обложка: Порядок выполнения скриптов в HTML. Тег script и атрибуты async, defer, module, nomodule и src

    С добавлением в JavaScript ES-модулей появилось не менее 24 способов подгрузить скрипты: с атрибутом src и без него; с async или без; defer или нет; type=module и nomodule. Все они немножко отличаются друг от друга.

    В этой статье сравним, как встроенные в HTML тэги обрабатываются в зависимости от набора атрибутов.

    Картинка вместо тысячи слов

    Сравнение порядка подгрузки и выполнения скриптов

    Мы видим, что async используется в legacy-скриптах, когда нужно выполнить их пораньше, а module — наоборот, чтобы задержать выполнение до подходящего момента (модульные скрипты по умолчанию обладают атрибутом defer).

    Шпаргалку сохранили, а теперь рассмотрим каждый из вариантов подробней.

    Сравнение обычного с async, defer и async defer

    Async и defer полностью поддерживаются и, как уже говорилось, интуитивно понятная разница между ними заключается в том, что скрипты с async выполняются сразу. Они не ждут окончания парсинга HTML, полного формирования DOM, а также подгрузки остальных скриптов.

    Обычные немодульные

    • Приостанавливают парсинг HTML.
    • Сразу подгружаются, парсятся и выполняются.
    • Гарантируют порядок выполнения относительно других обычных немодульных скриптов.
    • Блокируют событие DOMContentLoaded.
    • Учитывая всё вышесказанное, такие скрипты не подходят для некритичного кода, поскольку замедляют рендеринг и, как следствие, загрузку динамических веб-приложений.
    • Для встроенных (inline) немодульных скриптов defer игнорируется, и код выполняются сразу. Если defer прям сильно нужен, можно воспользоваться обходным путём с участием base64.
    • Для встроенных скриптов с указанием type=”module” defer применяется по умолчанию.
    • Подгружаются без остановки HTML-парсера.
    • Гарантируют порядок выполнения относительно других defer-скриптов (если они внешние — с атрибутом src). Криво работают в IE9.
    • Выполняются после окончания парсинга DOM (но перед срабатыванием DOMContentLoaded).
    • Блокируют событие DOMContentLoaded (только если скрипт не async defer).
    • Для встроенных (inline) немодульных скриптов async игнорируется.
    • Для встроенных модульных скриптов async поддерживается.
    • Подгружаются без остановки HTML-парсера.
    • Выполняются без очереди.
    • Не гарантируют порядок выполнения относительно других скриптов с async (также касается модульных скриптов с async).
    • Не ждут окончания парсинга HTML. Могут прервать построение DOM (в частности, когда он достаётся из кэша браузера).
    • Блокируют событие load (но не DOMContentLoad).
    • Не поддерживаются в IE9.

    Воспринимаются как async. В древних брузерных движках, которые не поддерживают async (IE9), работает так же, как defer.

    Сравнение type=module, type=text/javascript и nomodule

    Скрипты с type=module (также касается type=text/javascript)

    • Предполагают defer (также для встроенных скриптов, в отличие от немодульных скриптов).
    • Исходя из этого, гарантируют порядок выполнения относительно всех модульных скриптов, не использующих async (как встроенных, так и внешних).
    • Выполняются только раз, даже если скрипты с одинаковым src подгружаются несколько раз.
    • Могут использовать import для объявления зависимости с другими модульными скриптами (одна из причин, почему модули предполагают использование defer).
    • Подвергаются проверке CORS (в модулях из разных источников потребуется указать Access-Control-Allow-Origin: [источники]).
    • Не выполняются браузерами, которые не поддерживают модульные скрипты. Однако они всё ещё, по видимому, подгружаются в IE11, Firefox 52 ESR и т.д.

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

    Сравнение встроенных (inline) и внешних скриптов

    Встроенные скрипты (без атрибута src)

    • Для немодульных скриптов async и defer игнорируются.
    • Блокируют HTML-парсеры и построение DOM, так как выполняются сразу после загрузки.
    • Встроенные модульные скрипты предполагают defer. Также поддерживают async.
    • Не кэшируются браузерами.

    Внешние скрипты

    Кэшируются браузерами (при условии подходящих заголовков в ответе от сервера), поэтому могут использоваться в будущем без повторной подгрузки из сети.

    Источник

Оцените статью