arrow Главная arrow Статьи arrow

Порядок выполнения скриптов в HTML

img

28 октября, 2020

Порядок выполнения скриптов в HTML

Оригинальная статья: «<script> async, defer, async defer, module, nomodule, src, inline — the cheat sheet»

С добавлением в 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.
  • Не кэшируются браузерами.

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

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

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