Загрузка скриптов: async, defer, module

↑ 1.2 HTML · ← Предыдущая · → Следующая

Желательно~3 мин чтенияУровень: junior

Зачем это на собесе

Критично для производительности: как скрипты блокируют рендер и чем отличаются async и defer.

Объяснение

Обычный <script> блокирует парсинг HTML: браузер скачивает и выполняет его, затем продолжает.

АтрибутЗагрузкаВыполнениеПорядок
(нет)блокирует парсингсразупо порядку
asyncпараллельносразу после загрузки (может прервать парсинг)не гарантирован
deferпараллельнопосле парсинга, до DOMContentLoadedпо порядку
type="module"параллельнокак defer; строгий режим, свой scope, поддержка importпо порядку
nomoduleдля старых браузеров
<script src="analytics.js" async></script>      <!-- независимые скрипты -->
<script src="app.js" defer></script>            <!-- зависят от DOM/порядка -->
<script type="module" src="/src/main.js"></script>
<script type="module">import { init } from "./app.js"; init();</script>

Также: preload для критичных ресурсов, modulepreload, динамический import() для код-сплиттинга, fetchpriority="high", размещение скриптов в конце <body> (устаревший приём, заменён defer).

Нюансы и подводные камни

  • async для скриптов, зависящих друг от друга, приводит к гонкам порядка.
  • defer и async не работают для inline-скриптов (без src).
  • type="module" выполняется в строгом режиме, this на верхнем уровне — undefined.
  • Модули подчиняются CORS, при file:// могут не работать.
  • Сторонние скрипты (метрики, чаты) — главный источник блокировок: загружайте async или по бездействию.

Практика

  1. Сравните загрузку страницы с обычным, defer и async скриптом в Performance.
  2. Отложите загрузку виджета до requestIdleCallback.
  3. Добавьте preload для критичного шрифта/скрипта и измерьте LCP.

Вопросы с ответами

Связанные темы