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

Kate

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

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

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

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

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

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

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

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

Обычные немодульные <script>​

  • Приостанавливают парсинг HTML.
  • Сразу подгружаются, парсятся и выполняются.
  • Гарантируют порядок выполнения относительно других обычных немодульных скриптов.
  • Блокируют событие DOMContentLoaded.
  • Учитывая всё вышесказанное, такие скрипты не подходят для некритичного кода, поскольку замедляют рендеринг и, как следствие, загрузку динамических веб-приложений.

<script defer>​

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

<script async>​

  • Для встроенных (inline) немодульных скриптов async игнорируется.
  • Для встроенных модульных скриптов async поддерживается.
  • Подгружаются без остановки HTML-парсера.
  • Выполняются без очереди.
  • Не гарантируют порядок выполнения относительно других скриптов с async (также касается модульных скриптов с async).
  • Не ждут окончания парсинга HTML. Могут прервать построение DOM (в частности, когда он достаётся из кэша браузера).
  • Блокируют событие load (но не DOMContentLoad).
  • Не поддерживаются в IE9.

<script async defer>​

Воспринимаются как 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 и т.д.

<script nomodule>​

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

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

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

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

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

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

Примеры использования скриптов​

Шпаргалка по выполнению скриптов JavaScript

Источник статьи: https://tproger.ru/translations/script-execution-order/
 
Сверху