С добавлением в JavaScript ES-модулей появилось не менее 24 способов подгрузить скрипты: с атрибутом src и без него; с async
или без; defer
или нет; type=module
и nomodule
. Все они немножко отличаются друг от друга, — пишет tproger.ru в своем переводе статьи «<script> async, defer, async defer, module, nomodule, src, inline — the cheat sheet».
В этой статье сравним, как встроенные в 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
. - Не кэшируются браузерами.
Внешние скрипты
Кэшируются браузерами (при условии подходящих заголовков в ответе от сервера), поэтому могут использоваться в будущем без повторной подгрузки из сети.
Примеры использования скриптов
[customscript]techrocks_custom_after_post_html[/customscript]
[customscript]techrocks_custom_script[/customscript]