Прямой ответ
Ресурс блокирует отрисовку, если браузер не может отобразить содержимое до его получения и обработки. Скрипты и стили в заголовке документа блокируют отрисовку по умолчанию.
| Ресурс | Поведение по умолчанию | Способ разблокировки |
|---|---|---|
| Скрипт без атрибутов | Блокирует разбор документа | defer либо async |
| Стили в заголовке | Блокирует отрисовку | Асинхронная загрузка |
| Шрифт | Не блокирует, но влияет на отрисовку текста | Предзагрузка |
<!-- Скрипт выполняется после разбора документа -->
<script src="/app.js" defer></script>
<!-- Стили загружаются асинхронно -->
<link rel="stylesheet" href="/styles.css" media="print"
onload="this.media='all'">
Атрибут defer откладывает выполнение скрипта до завершения разбора документа, сохраняя порядок выполнения. О выделении критических стилей — в статье про критический CSS.
Различие defer и async, порядок выполнения
Два атрибута изменяют момент выполнения скрипта, и различие определяет применимость.
defer загружает скрипт параллельно с разбором документа, а выполняет после завершения разбора, сохраняя порядок следования в документе. Применяется к скриптам, которым требуется готовый документ.
async загружает скрипт параллельно и выполняет сразу после загрузки, независимо от состояния разбора. Порядок выполнения не гарантируется, поэтому применяется к независимым скриптам.
Практическое следствие: скрипты, зависящие от порядка выполнения либо от готовности документа, загружаются с defer. Независимые скрипты, например счётчики, — с async.
Отдельный аспект — стили. Стили в заголовке блокируют отрисовку, поскольку браузер не отображает содержимое до применения правил, влияющих на макет. Асинхронная загрузка стилей исключает блокировку, но требует критического CSS для первого экрана.
Три признака некорректного устранения блокировки.
Скрипт с async, зависящий от порядка. Выполнение может произойти раньше загрузки зависимости, поэтому возникает ошибка.
Стили без критического CSS. Асинхронная загрузка приводит к отрисовке без оформления до применения стилей.
Устранение блокировки для критичного скрипта. Скрипт, необходимый для работы страницы, выполняется позже, поэтому взаимодействие недоступно.
О метриках времени отрисовки — в статье про FCP и LCP; о предзагрузке ресурсов — в статье про preload и prefetch.
Проверка выполняется по фактическому времени отрисовки, поскольку изменение порядка загрузки может не дать ожидаемого эффекта.
Влияние на взаимодействие и порядок инициализации
Устранение блокировки отрисовки изменяет момент выполнения скриптов, поэтому инициализация страницы требует внимания.
Готовность документа. Скрипт с отложенным выполнением запускается после разбора, поэтому элементы документа доступны. Скрипт без атрибутов выполняется во время разбора, поэтому часть элементов может отсутствовать.
Порядок инициализации. Скрипты с отложенным выполнением запускаются в порядке следования, поэтому зависимость между ними сохраняется. Смешение отложенных и асинхронных скриптов нарушает предсказуемость порядка.
Доступность взаимодействия. Страница отрисована, но обработчики ещё не подключены, поэтому действие пользователя не обрабатывается. Момент подключения обработчиков учитывается при оценке интерактивности.
Практическое следствие: устранение блокировки отрисовки сокращает время появления содержимого, но требует проверки момента, когда взаимодействие становится доступным.
Практика: устранение блокирующих ресурсов
1. Определите блокирующие ресурсы. Скрипты без атрибутов и стили в заголовке блокируют отрисовку по умолчанию.
2. Используйте defer для скриптов, зависящих от документа. Выполнение происходит после разбора, порядок сохраняется.
3. Используйте async для независимых скриптов. Порядок выполнения не гарантируется, поэтому скрипт должен быть независимым.
4. Загружайте стили асинхронно. Отрисовка не ждёт полного файла стилей, но требует критического CSS для первого экрана.
5. Обеспечьте критический CSS. Стили первого экрана встраиваются в документ, поэтому отрисовка выполняется оформленной.
6. Не устраняйте блокировку для критичных скриптов. Скрипт, необходимый для работы страницы, выполняется в требуемый момент.
7. Проверяйте фактическое время отрисовки. Изменение порядка загрузки проверяется измерением, а не предположением.
Рекомендации:
- Различайте блокировку разбора и блокировку отрисовки. Скрипт блокирует разбор документа, стили — отрисовку содержимого.
- Учитывайте порядок выполнения. Скрипты с
deferвыполняются в порядке следования, сasync— по готовности. - Проверяйте зависимость скриптов. Скрипт, зависящий от другого, не загружается с
async, поскольку порядок не гарантируется. - Сочетайте асинхронные стили с критическим CSS. Без критических стилей страница кратковременно отображается без оформления.
- Пересматривайте при изменении состава ресурсов. Новый скрипт либо стиль может восстановить блокировку отрисовки.
Практический сценарий: отрисовка задерживалась из-за скрипта в заголовке
Контекст: время первой отрисовки было высоким, при этом содержимое страницы было простым.
- Диагностика. Скрипт в заголовке документа загружался без атрибутов, поэтому разбор документа приостанавливался до получения и выполнения скрипта. Отрисовка начиналась после завершения выполнения.
- Причина. Скрипт не требовался для отрисовки первого экрана, но блокировал разбор документа. Порядок выполнения не был критичен, поэтому атрибут не применялся.
- Действия. (а) Перенесли скрипт в конец документа с атрибутом
defer, чтобы выполнение происходило после разбора; (б) перевели загрузку стилей в асинхронный режим с встройкой критического CSS; (в) проверили, что взаимодействие доступно после выполнения скрипта; (г) измерили время отрисовки до и после изменения; (д) зафиксировали правило размещения скриптов в конфигурации сборки. - Результат. Время первой отрисовки сократилось, поскольку разбор документа не приостанавливается загрузкой скрипта.
Вывод: скрипт в заголовке без атрибутов блокирует разбор документа, поэтому отрисовка задерживается. Атрибут defer сохраняет порядок выполнения и исключает блокировку разбора.
FAQ
Какие ресурсы блокируют отрисовку? Скрипты без атрибутов блокируют разбор документа, стили в заголовке блокируют отрисовку содержимого. Шрифты не блокируют отрисовку, но влияют на отображение текста.
Чем defer отличается от async?
defer выполняет скрипт после разбора документа, сохраняя порядок. async выполняет сразу после загрузки, порядок не гарантируется.
Можно ли загружать стили асинхронно? Да, но требуется критический CSS для первого экрана, иначе страница кратковременно отображается без оформления.
Как проверить устранение блокировки? Измерением времени отрисовки до и после изменения. Фактический результат может отличаться от ожидаемого.
Источники: MDN script async, MDN link, MDN Performance — ссылки в шапке.