Скорость первой отрисовки: сравниваем подходы в CSS и JS
Один и тот же лендинг рисуется за 0,8 с и за 3,4 с в зависимости от того, как подключены стили и скрипты. Разбираем, чем платит каждый подход к ускорению First Contentful Paint.

Возьмите обычный лендинг: заголовок, картинка героя, форма подписки, аналитика и виджет чата. На среднем 4G и Moto G4 (эмуляция в Lighthouse) он показывает First Contentful Paint в районе 3,4 секунды — потому что браузер сначала тянет весь bundle.css на 180 КБ и синхронный app.js, и только потом рисует хоть что-то. Тот же лендинг после переработки подключения ресурсов даёт FCP около 0,8 секунды. Ничего не выкидывали — переставили порядок и способ загрузки.
Разберём подходы к ускорению первой отрисовки со стороны CSS и JavaScript, у каждого назовём цену: что выигрываете в миллисекундах и чем платите в сложности или риске мигания.
Что именно тормозит первую отрисовку
FCP наступает, когда браузер отрисовал первый непустой кадр. До этого он строит DOM и CSSOM. И вот где ловушки:
- CSS блокирует рендеринг. Браузер не покажет ничего, пока не загрузит и не распарсит все стили в
<head>, подключённые через<link rel="stylesheet">. Это by design: иначе пользователь увидел бы нестилизованный текст (FOUC). - Синхронный JavaScript блокирует и парсинг HTML, и рендеринг. Тег
<script src>без атрибутов останавливает построение DOM на время загрузки и выполнения скрипта. - Шрифты откладывают текст. При
font-display: auto(значение по умолчанию) браузер прячет текст до трёх секунд, ожидая веб-шрифт.
Дальше — конкретные способы снять эти блокировки.
Подход 1. Критический CSS инлайном
Идея: вытащить стили, нужные для отрисовки первого экрана, и вставить их прямо в <style> внутри <head>. Остальной CSS грузить асинхронно.
<head>
<style>
/* только то, что видно без прокрутки */
body { margin: 0; font-family: system-ui, sans-serif; }
.hero { min-height: 60vh; background: #0d1b2a; color: #fff; }
.hero h1 { font-size: 2.5rem; padding: 2rem; }
</style>
<!-- полный CSS грузим не блокируя рендер -->
<link rel="preload" href="/main.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/main.css"></noscript>
</head>Трюк с rel="preload" и переключением на rel='stylesheet' в onload — это паттерн из проекта loadCSS от Filament Group. Браузер грузит файл в фоне, а применяет как стиль только после загрузки, не блокируя первую отрисовку.
Цена. Критический CSS надо извлекать и держать в актуальном состоянии. Руками это делать больно — стили меняются, а инлайн отстаёт. На сборке помогают инструменты вроде critical или penthouse (проверьте актуальную версию в их репозиториях). Второй риск: если критический набор собран неаккуратно, первый экран мигнёт при подгрузке основного CSS.
Подход 2. defer и async для скриптов
Синхронный <script> в <head> — самая частая причина медленного FCP на сайтах, где кто-то вставил аналитику или библиотеку «наверх, чтобы точно загрузилась».
| Способ | Блокирует парсинг HTML | Порядок выполнения | Когда выполняется |
|---|---|---|---|
обычный <script> | да | по порядку в документе | сразу при встрече тега |
async | нет (грузится параллельно) | не гарантирован | сразу после загрузки файла |
defer | нет | по порядку в документе | после парсинга DOM, до DOMContentLoaded |
type="module" | нет | по порядку (модули defer по умолчанию) | после парсинга DOM |
Правило простое:
- defer — для скриптов, которым нужен готовый DOM и важен порядок (ваш
app.js, зависимости). - async — для независимых скриптов, которым плевать на DOM и друг на друга: счётчики, теги аналитики.
Один синхронный скрипт на 60 КБ в<head>откладывает первую отрисовку ровно на время его загрузки плюс выполнения. Перенос наdeferнередко срезает FCP на секунду и больше без единой строчки нового кода.
Цена. С async вы теряете контроль над порядком: если скрипт А ждёт скрипт Б, async сломает их. С defer код выполнится позже — если у вас была логика, завязанная на выполнение до отрисовки, поведение изменится.
Подход 3. Приоритеты и предзагрузка ресурсов
Браузер сам расставляет приоритеты загрузки, но иногда ошибается — например, поздно узнаёт про шрифт, который нужен для первого экрана.
<link rel="preload" as="font" crossorigin>— сообщает браузеру про критический шрифт заранее. Атрибутcrossoriginдля шрифтов обязателен, иначе файл скачается дважды.<link rel="preconnect">— заранее открывает соединение к стороннему домену (CDN, домен аналитики), экономя время на DNS и TLS.- Атрибут
fetchpriority="high"на теге<img>героя — поднимает картинку первого экрана в очереди. Поддержка есть в актуальных Chromium-браузерах; в остальных просто игнорируется.
<link rel="preconnect" href="https://cdn.example.com">
<link rel="preload" href="/fonts/inter.woff2"
as="font" type="font/woff2" crossorigin>
<img src="/hero.avif" fetchpriority="high" alt="..." width="1200" height="600">Цена. preload — оружие точечное. Если предзагрузить лишнее, вы отнимете полосу у действительно критичных ресурсов и сделаете хуже. В консоли Chrome появится предупреждение «The resource was preloaded but not used» — это сигнал, что preload лишний.
Подход 4. Шрифты без скачков и невидимого текста
Веб-шрифт по умолчанию прячет текст, пока грузится. Ставьте font-display: swap — браузер сразу покажет текст системным шрифтом и заменит его на веб-шрифт при загрузке.
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap;
font-weight: 400;
}Цена. swap вызывает FOUT — заметный скачок при подмене шрифта, особенно если метрики системного и веб-шрифта сильно расходятся. Компромисс — font-display: optional: браузер использует веб-шрифт только если тот успел загрузиться быстро, иначе оставляет системный. Скачка нет, но и веб-шрифт на первой загрузке может не примениться.
Когда что выбирать
- Лендинг, важен первый экран — критический CSS инлайном плюс
preloadшрифта и картинки героя. Максимальный эффект, оправданная возня со сборкой. - Контентный сайт, много страниц — начните с
defer/asyncна скриптах иfont-display: swap. Дёшево, безопасно, покрывает большинство проблем. - SPA — здесь FCP упирается в размер JS-бандла; критический CSS помогает меньше, чем разбиение бандла и SSR/пре-рендер первого экрана. Это отдельная тема, здесь её не разворачиваем.
Что ломается
- Инлайн критического CSS раздувает HTML. Если критический набор перевалил за десятки килобайт, вы жертвуете временем до первого байта ради отрисовки — иногда невыгодно.
- async на зависимых скриптах ломает порядок. Классика:
asyncна скрипте, который использует jQuery, загруженный тоже черезasync. Периодические ошибки «$ is not defined» — оттуда. - preload без crossorigin для шрифта приводит к двойной загрузке файла — предзагруженный не совпадает по режиму CORS с тем, что запрашивает
@font-face.
Что делать дальше
Замеряйте, а не гадайте. Откройте Lighthouse в Chrome DevTools в режиме мобильной эмуляции с троттлингом сети и снимите FCP до и после каждого изменения — иначе не поймёте, что именно помогло. Для боевого сайта смотрите на поле: Core Web Vitals в Search Console и метрику LCP (Largest Contentful Paint) — она ближе к тому, что чувствует пользователь, чем FCP. Начните с самого дешёвого шага — переведите синхронные скрипты в <head> на defer — и измерьте эффект перед тем, как браться за критический CSS.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: web.dev — First Contentful Paint (FCP), MDN — <script>: async и defer
Частые вопросы
У меня старый браузер без поддержки fetchpriority — что будет?
Я перенёс скрипты на defer, а часть функций перестала работать. Почему?
Критический CSS собирается автоматически или это делают руками?
После свапа шрифта текст заметно дёргается. Как убрать скачок?
Стоит ли preload'ить весь CSS, чтобы точно грузился быстрее?
Какую метрику мерить в проде — FCP или что-то ещё?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.