Скрипты — атрибуты async и defer
23.05.2022
Теги: Frontend • JavaScript • Web-разработка • Документ • Теория
Когда браузер загружает HTML и доходит до тега <script>…<script>, он не может продолжать строить DOM. Он должен сначала выполнить скрипт. То же самое происходит и с внешними скриптами <script src="…"></script> — браузер должен подождать, пока загрузится скрипт, выполнить его, и только затем обработать остальную страницу.
Это ведёт к двум важным проблемам:
- Скрипты не видят DOM-элементы ниже себя, поэтому к ним нельзя добавить обработчики и т.д.
- Если вверху страницы объёмный скрипт, он «блокирует» страницу на время загрузки и выполнения.
Конечно, есть пути, как это обойти. Например, мы можем поместить скрипт внизу страницы. Тогда он сможет видеть элементы над ним и не будет препятствовать отображению содержимого страницы.
<body> <header>...</header> <main>...</main> <footer>...</footer> <script src="..."></script> </body>
Но это решение далеко от идеального. Например, браузер замечает скрипт (и может начать загружать его) только после того, как он полностью загрузил HTML-документ. В случае с длинными HTML-страницами это может создать заметную задержку.
К счастью, есть два атрибута тега <script>, которые решают нашу проблему — defer и async.
defer
Атрибут defer сообщает браузеру, что он должен продолжать обрабатывать страницу и загружать скрипт в фоновом режиме, а затем запустить этот скрипт, когда DOM дерево будет полностью построено. Важный момент — скрипт с атрибутом defer всегда выполняются, когда дерево DOM готово, но до события DOMContentLoaded.
Отложенные с помощью defer скрипты сохраняют порядок относительно друг друга, как и обычные скрипты. Поэтому, если сначала в html-коде идет большой скрипт, а затем маленький — то последний будет ждать окончания загрузки и выполнения первого. Спецификация требует последовательного выполнения скриптов согласно порядку в документе.
<script defer src="large.js"></script> <script defer src="small.js"></script>
async
Атрибут async означает, что скрипт абсолютно независим:
- Содержимое страницы отображается сразу же —
asyncего не блокирует. DOMContentLoadedможет произойти как до, так и послеasync, никаких гарантий нет.- Асинхронные скрипты не ждут друг друга. Меньший скрипт
small.jsидёт вторым, но скорее всего загрузится раньшеlarge.js, поэтому и запустится первым. То есть, скрипты выполняются в порядке загрузки.
Асинхронные скрипты очень полезны для добавления на страницу сторонних скриптов — счётчиков, рекламы и т.д. Они не зависят от наших скриптов, и мы тоже не должны ждать их.
<!-- Типичное подключение скрипта Google Analytics --> <script async src="https://google-analytics.com/analytics.js"></script>
Динамически загружаемые скрипты
Мы можем также добавить скрипт и динамически, с помощью js-кода:
let script = document.createElement('script'); script.src = 'large.js'; document.body.append(script);
Динамически загружаемый скрипт по умолчанию ведет себя как «async» — начинает загружаться, как только он будет добавлен в документ:
- Сам скрипт никого не ждет, и его никто не ждёт
- Если первым загрузился — первым и запустился
Мы можем изменить относительный порядок скриптов с «первый загрузился — первый выполнился» на порядок, в котором они идут в документе (как в обычных скриптах) с помощью явной установки свойства async в false.
function loadScript(src) { let script = document.createElement('script'); script.src = src; script.async = false; document.body.append(script); } // large.js запускается первым, так как async=false loadScript('large.js'); loadScript('small.js');
Поиск: Frontend • JavaScript • Web-разработка • Документ • Теория
