3. Critical Rendering Path
Critical Rendering Path (CRP) — это последовательность шагов, которые браузер выполняет для превращения HTML/CSS/JS в пиксели на экране.
Как браузер рендерит страницу?
Заголовок раздела «Как браузер рендерит страницу?»HTML → DOM → CSSOM → Render Tree → Layout → Paint → Composite- HTML Parsing → DOM (Document Object Model)
- CSS Parsing → CSSOM (CSS Object Model)
- DOM + CSSOM → Render Tree (только видимые элементы)
- Layout → вычисление позиций и размеров
- Paint → рисование пикселей
- Composite → наложение слоёв
Блокирующие ресурсы
Заголовок раздела «Блокирующие ресурсы»CSS блокирует рендеринг
Заголовок раздела «CSS блокирует рендеринг»Браузер НЕ будет рисовать страницу, пока не загрузит весь CSS.
<!-- ❌ Блокирует рендеринг --><link rel="stylesheet" href="styles.css">
<!-- ✅ Critical CSS inline --><style> /* Только стили для первого экрана */ body { font-family: sans-serif; margin: 0; } .hero { background: #000; color: #fff; padding: 40px; }</style>
<!-- ✅ Non-critical CSS загружаем асинхронно --><link rel="preload" href="full-styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'"><noscript><link rel="stylesheet" href="full-styles.css"></noscript>JavaScript блокирует парсинг HTML
Заголовок раздела «JavaScript блокирует парсинг HTML»По умолчанию JS останавливает HTML-парсинг.
<!-- ❌ Блокирует HTML парсинг --><script src="app.js"></script>
<!-- ✅ Defer — выполняется после парсинга HTML --><script src="app.js" defer></script>
<!-- ✅ Async — загружается параллельно, выполняется сразу --><script src="analytics.js" async></script>
<!-- ✅ Module — автоматически defer --><script type="module" src="app.js"></script>Разница defer vs async:
defer— сохраняет порядок, выполняется после DOM готовasync— не гарантирует порядок, для независимых скриптов
Preload, Prefetch, Preconnect
Заголовок раздела «Preload, Prefetch, Preconnect»<!-- Предзагрузка критических ресурсов --><link rel="preload" href="/hero.webp" as="image" fetchpriority="high"><link rel="preload" href="/main.css" as="style"><link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin>
<!-- Предподключение к внешним серверам --><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://api.example.com" crossorigin>
<!-- Предварительная загрузка следующей страницы --><link rel="prefetch" href="/next-page.html"><link rel="prefetch" href="/next-page-image.jpg" as="image">Reflow и Repaint
Заголовок раздела «Reflow и Repaint»Reflow (Layout) — пересчёт размеров и позиций. Дорогая операция.
Repaint — перерисовка пикселей. Дешевле reflow.
// ❌ Вызывает множественные reflow (thrashing)for (let i = 0; i < 100; i++) { element.style.left = element.offsetLeft + 1 + 'px'; // read + write = reflow}
// ✅ Читаем, потом пишем (batch)const positions = elements.map(el => el.offsetLeft); // все readselements.forEach((el, i) => { el.style.left = positions[i] + 1 + 'px'; // все writes});
// ✅ Используем transform вместо left/top (не вызывает reflow!)element.style.transform = 'translateX(100px)';
// ✅ Оффскрин манипуляцииconst fragment = document.createDocumentFragment();items.forEach(item => { const li = document.createElement('li'); li.textContent = item; fragment.appendChild(li);});list.appendChild(fragment); // один reflowЧто вызывает Reflow?
Заголовок раздела «Что вызывает Reflow?»- Изменение
width,height,margin,padding,border - Добавление/удаление элементов в DOM
- Изменение текстового контента
- Изменение класса элемента
- Чтение layout-свойств:
offsetTop,scrollTop,getBoundingClientRect()
Что НЕ вызывает Reflow?
Заголовок раздела «Что НЕ вызывает Reflow?»opacity— только compositingtransform— только compositingfilter— только compositingwill-change: transform— выносит в отдельный GPU слой
Оптимизация CSS
Заголовок раздела «Оптимизация CSS»/* ✅ Используйте transform для анимаций */.animated { transform: translateX(0); transition: transform 0.3s ease;}.animated:hover { transform: translateX(10px); /* Не вызывает reflow */}
/* ✅ will-change для GPU слоёв (используйте осторожно!) */.hero-animation { will-change: transform, opacity;}
/* ❌ Не используйте will-change везде — жрёт память */.bad { will-change: all; }
/* ✅ Containment — изолируйте область рендеринга */.widget { contain: layout style paint;}