Почему ресурсы CSS и JavaScript блокируют построение и отображение страницы
Что проверяют
Знаете ли вы критический путь рендеринга и различаете ли блокировку разбора разметки и блокировку отрисовки. Это базовый вопрос про производительность: по нему видно, понимаете ли вы, почему страница белая первые секунды, или заучили список советов.
Как отвечать
Браузер разбирает HTML потоково и строит DOM. Встретив <link rel="stylesheet">, он продолжает разбирать разметку, но не рисует ничего, пока стили не загружены и не разобраны в CSSOM: без стилей нельзя посчитать геометрию, а показать неоформленную страницу и тут же перерисовать хуже, чем подождать. То есть CSS блокирует отрисовку, а не построение DOM.
Обычный <script> без атрибутов блокирует и разбор тоже: скрипт может изменить разметку через document.write, поэтому построение DOM останавливается, пока файл скачивается и выполняется. Больше того, выполнение скрипта ждёт загрузки стилей, объявленных выше по документу, потому что код может спросить getComputedStyle.
Отсюда набор мер:
defer: загрузка параллельно разбору, выполнение после построения DOM, порядок файлов сохраняется;async: загрузка параллельно, выполнение сразу по готовности, порядок не гарантирован, годится для независимых счётчиков;type="module"откладывается по умолчанию, какdefer;- критический CSS встроить в
<head>, остальное вынести; - стили для печати пометить атрибутом
media, тогда они не держат отрисовку; preloadиpreconnectдля шрифтов и ключевых ресурсов.
Где спотыкаются
Говорят, что CSS блокирует разбор HTML. Нет, DOM строится дальше, останавливается именно отрисовка, и это легко проверить: разметка ниже подключённых стилей всё равно разбирается.
Путают defer и async и ставят второй на скрипты с зависимостями. Два асинхронных файла могут выполниться в любом порядке, и библиотека окажется недоступна коду, который на неё рассчитывает.
Считают, что скрипт в конце <body> равносилен defer. Разбор он действительно не блокирует, но только потому, что разбирать уже нечего: загрузка начинается позже, а defer качает файл параллельно с самого начала.