> Почему ресурсы CSS и JavaScript блокируют построение и отображение страницы (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Rutube

Стек: JavaScript

> Пример ответа

Короткий ответ

CSS и JavaScript блокируют построение и отображение страницы, потому что браузер обрабатывает их синхронно в критическом пути рендеринга. CSS блокирует рендеринг, так как браузер не показывает частично стилизованный контент (flash of unstyled content). JavaScript блокирует построение DOM, поскольку может изменять DOM и CSSOM, и браузер приостанавливает парсинг HTML до полного выполнения скрипта.

Подробное объяснение

Критический путь рендеринга состоит из нескольких этапов: парсинг HTML в DOM, парсинг CSS в CSSOM, объединение в render tree, layout и paint. CSS и JavaScript влияют на этот процесс следующим образом:

CSS (CSSOM):

  • CSS считается render-blocking ресурсом: браузер не начинает рендеринг, пока не построит CSSOM полностью.
  • Это сделано для предотвращения FOUC (flash of unstyled content) - пользователь не должен видеть страницу без стилей.
  • CSSOM строится синхронно: пока не загружен и не распарсен весь CSS, браузер не переходит к следующему шагу.

JavaScript (DOM):

  • По умолчанию <script> без атрибутов async или defer является parser-blocking: браузер останавливает парсинг HTML, загружает и выполняет скрипт, и только потом продолжает.
  • Это необходимо, так как скрипт может:
    • Изменять DOM (например, document.write())
    • Запрашивать стили (например, getComputedStyle()), что требует готового CSSOM
  • Если скрипт выполняется до завершения CSSOM, браузер блокирует его выполнение до загрузки CSS - это называется CSS-блокировка JavaScript.

Взаимодействие:

  • Если JavaScript встречается до CSS, он может быть отложен до загрузки CSS (если скрипт запрашивает стили).
  • Если CSS загружается после JavaScript, скрипт может выполняться, но рендеринг всё равно будет заблокирован до полной загрузки CSS.

На практике

Для оптимизации производительности загрузки страницы используются следующие подходы:

  1. Критический CSS: встраивание минимального набора стилей для первого экрана непосредственно в <head> через <style>, остальной CSS загружается асинхронно с помощью media="print" onload="this.media='all'" или через rel="preload".

  2. Асинхронная загрузка JavaScript:

    • async: скрипт загружается параллельно с парсингом HTML и выполняется сразу после загрузки, блокируя парсинг.
    • defer: скрипт загружается параллельно, но выполняется только после завершения парсинга HTML, в порядке появления в документе.
  3. Размещение скриптов в конце <body>: классический подход, когда скрипты не блокируют построение DOM, но всё ещё блокируют рендеринг после выполнения.

  4. Preload и prefetch: для предварительной загрузки критических ресурсов (CSS, шрифты) с помощью <link rel="preload">.

  5. Code splitting: разделение JavaScript на чанки, загружаемые по требованию (lazy loading).

Пример кода

HTML
<!DOCTYPE html>
<html>
<head>
<!-- Критический CSS встроен -->
<style>
body { font-family: sans-serif; margin: 0; padding: 20px; }
.header { background: #333; color: white; padding: 10px; }
</style>
<!-- Некритический CSS загружается асинхронно -->
<link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
<!-- Defer - выполнится после парсинга HTML -->
<script src="app.js" defer></script>
<!-- Async - выполнится сразу после загрузки, не гарантирует порядок -->
<script src="analytics.js" async></script>
</head>
<body>
<div class="header">Заголовок</div>
<p>Контент страницы</p>
<!-- Скрипты в конце body - классический подход -->
<script src="non-critical.js"></script>
</body>
</html>

Как отвечать на собеседовании

Начни с краткого определения критического пути рендеринга. Объясни, что CSS блокирует рендеринг из-за необходимости полного CSSOM, а JavaScript - парсинг DOM из-за возможных модификаций. Упомяни разницу между render-blocking и parser-blocking. Приведи примеры оптимизаций: critical CSS, async/defer, preload. Покажи понимание компромиссов: async нарушает порядок выполнения, defer задерживает выполнение до конца парсинга. Если спросят про метрики, свяжи с First Paint, First Contentful Paint и Time to Interactive.

Что проверяет интервьюер

  • Понимание критического пути рендеринга и его этапов
  • Знание разницы между DOM и CSSOM и их влияния на рендеринг
  • Умение объяснять, почему браузер блокирует рендеринг (FOUC, консистентность)
  • Практические навыки оптимизации загрузки (critical CSS, async/defer, preload)
  • Понимание компромиссов между производительностью и функциональностью

Типичные ошибки

  • Путать render-blocking (CSS) и parser-blocking (JavaScript) - это разные механизмы
  • Считать, что async и defer полностью решают проблему блокировки - они влияют только на парсинг HTML, но не на рендеринг
  • Не учитывать, что JavaScript может быть заблокирован CSS (CSS-блокировка скриптов)
  • Предлагать встраивать весь CSS в <head> - это увеличивает размер HTML и время первого байта
  • Игнорировать влияние шрифтов и изображений на рендеринг (они тоже могут блокировать paint)

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью