Процесс построения веб-страницы
Last updated
Last updated
Когда браузер создает DOM, если он встречает тег script
в HTML, он должен выполнить его сразу. Если скрипт является внешним, он должен сначала загрузить скрипт.
По умолчанию, чтобы выполнить скрипт, построение DOM приостанавливается и возобновляется только после того, как JavaScript-движок выполнил скрипт.
Почему построение DOM приостанавливается? Скрипты могут изменять как HTML, так и его продукт - DOM, добавляя узлы. Скрипты могут также запрашивать что-то о DOM и если это происходит, когда DOM все еще строится, он может вернуть неожиданные результаты.
JavaScript блокирует построение DOM, поскольку он может модифицировать документ. CSS не может изменить документ, поэтому кажется, что нет причин для его блокировки, правильно?
Однако, что если скрипт запрашивает информацию (о стиле), которая еще не была проанализирована? Браузер не знает, что собирается выполнить скрипт - он может запросить что-то вроде фонового цвета DOM-узла, который зависит от таблицы стилей.
Из-за этого CSS может блокировать разбор HTML в зависимости от порядка внешних таблиц стилей и сценариев в документе. Если перед скриптами в документе есть внешние таблицы стилей, конструкция объектов DOM и CSS может мешать друг другу.
Когда синтаксический анализатор попадает в тег сценария, конструкция DOM не может продолжаться до тех пор, пока JavaScript не завершит выполнение и JavaScript не будет выполнен до тех пор, пока CSS не будет загружен, проанализирован и не будет доступен CSS.
Еще одна вещь о которой следует помнить, заключается в том, что даже если CSS не блокирует конструкцию DOM, он блокирует рендеринг. Браузер ничего не отобразит, пока не будет DOM и CSS. Это связано с тем, что страницы без CSS часто непригодны для использования. Если браузер показал беспорядочную страницу без CSS, а через несколько мгновений стилизованную - это называется Flash of Unstyled Content.
Внезапные смещения содержимого и визуальные изменения плохо влияют на UX (user experience). Чтобы обойти эти проблемы, вы должны стремиться как можно быстрее доставить CSS. Помните правило «стили наверху, сценарии внизу»? Теперь вы знаете зачем делать именно так.
Синхронные скрипты, блокирующие парсер - это проблема. И не все скрипты одинаково важны для пользователей, например для отслеживания и аналитики. Решение? Асинхронная загрузка этих менее важных скриптов.
Атрибуты defer
и async
были введены, чтобы дать разработчикам возможность рассказать браузеру, какие скрипты обрабатывать асинхронно.
Оба атрибута сообщают браузеру, что он может продолжить разбор HTML при загрузке сценария в фоновом режиме, а затем выполнить скрипт после его загрузки. Таким образом, загрузка скриптов не блокирует конструкцию DOM и рендеринг страниц. Результат: пользователь может видеть страницу до того, как все сценарии завершили загрузку.
Разница между ними - это тот момент, когда загруженные скрипты начинают выполняться.
Выполнение defer
скриптов начинается после завершения парсинга, но перед событием DOMContentLoaded
. Это гарантирует, что скрипты будут выполняться в том порядке, в котором они отображаются в HTML и не будут блокировать синтаксический анализатор.
async
выполняется при первой возможности после завершения загрузки и перед событием загрузки окна. Это означает, что возможно скрипты не выполняются в том порядке, в котором они отображаются в HTML. Это также означает, что они могут прервать создание DOM.
Где бы они ни были указаны, асинхронные скрипты загружаются с низким приоритетом. Они часто загружаются после всех других скриптов, не блокируя создание DOM. Однако, если async
скрипт завершает загрузку раньше, его выполнение может блокировать создание DOM и все синхронные скрипты, которые впоследствии завершают загрузку.