В предыдущем модуле была изучена некоторая теория, лежащая в основе критического пути рендеринга , и то, как ресурсы, блокирующие рендеринг и блокирующие парсер, могут задержать начальный рендеринг страницы. Теперь, когда вы понимаете часть теории, стоящей за этим, вы готовы изучить некоторые методы оптимизации критического пути рендеринга.
При загрузке страницы в ее HTML-коде упоминается множество ресурсов, которые обеспечивают странице ее внешний вид и макет посредством CSS, а также ее интерактивность посредством JavaScript. В этом модуле рассматривается ряд важных концепций, связанных с этими ресурсами, и то, как они влияют на время загрузки страницы.
Блокировка рендеринга
Как обсуждалось в предыдущем модуле , CSS является ресурсом , блокирующим рендеринг , поскольку он блокирует рендеринг браузером любого контента до тех пор, пока не будет построена объектная модель CSS (CSSOM) . Браузер блокирует рендеринг, чтобы предотвратить Flash of Unstyled Content (FOUC) , что нежелательно с точки зрения пользовательского опыта.
В предыдущем видео есть краткий FOUC, где вы можете увидеть страницу без какого-либо стиля. Впоследствии все стили применяются после того, как CSS страницы завершает загрузку из сети, и нестилизованная версия страницы немедленно заменяется стилизованной версией.
Вообще говоря, FOUC — это то, что вы обычно не видите, но эту концепцию важно понимать, чтобы знать, почему браузер блокирует рендеринг страницы, пока CSS не будет загружен и применен к странице. Блокировка рендеринга не обязательно нежелательна, но вы хотите минимизировать ее продолжительность, поддерживая оптимизацию CSS.
Блокировка парсера
Ресурс , блокирующий парсер, прерывает парсер HTML, например элемент <script>
без атрибутов async
или defer
. Когда парсер встречает элемент <script>
, браузеру необходимо оценить и выполнить скрипт, прежде чем продолжить парсинг остальной части HTML. Это сделано намеренно, так как скрипты могут изменять или получать доступ к DOM в то время, пока он все еще строится.
<!-- This is a parser-blocking script: -->
<script src="/script.js"></script>
При использовании внешних файлов JavaScript (без async
или defer
) парсер блокируется с момента обнаружения файла до момента его загрузки, анализа и выполнения. При использовании встроенного JavaScript парсер аналогичным образом блокируется до тех пор, пока встроенный скрипт не будет проанализирован и выполнен.
Сканер предварительной загрузки
Сканер предварительной загрузки — это оптимизация браузера в форме вторичного HTML-парсера, который сканирует необработанный HTML-ответ для поиска и спекулятивного извлечения ресурсов до того, как основной HTML-парсер их обнаружит. Например, сканер предварительной загрузки позволит браузеру начать загрузку ресурса, указанного в элементе <img>
, даже если HTML-парсер заблокирован при извлечении и обработке таких ресурсов, как CSS и JavaScript.
Чтобы воспользоваться сканером предварительной загрузки, критические ресурсы должны быть включены в HTML-разметку, отправленную сервером. Следующие шаблоны загрузки ресурсов не обнаруживаются сканером предварительной загрузки:
- Изображения, загруженные CSS с использованием свойства
background-image
. Эти ссылки на изображения находятся в CSS и не могут быть обнаружены сканером предварительной загрузки. - Динамически загружаемые скрипты в виде разметки элемента
<script>
, внедряемые в DOM с помощью JavaScript, или модули, загружаемые с помощью динамическогоimport()
. - HTML, отрендеренный на клиенте с помощью JavaScript. Такая разметка содержится в строках в ресурсах JavaScript и не обнаруживается сканером предварительной загрузки.
- Объявления CSS
@import
.
Все эти шаблоны загрузки ресурсов являются поздно обнаруженными ресурсами, и поэтому не получают выгоды от сканера предварительной загрузки. Избегайте их, когда это возможно. Однако, если избежать таких шаблонов невозможно , вы можете использовать подсказку preload
, чтобы избежать задержек обнаружения ресурсов.
CSS
CSS определяет представление и макет страницы. Как было описано ранее, CSS является ресурсом, блокирующим рендеринг, поэтому оптимизация CSS может оказать значительное влияние на общее время загрузки страницы.
Минификация
Минификация CSS- файлов уменьшает размер файла CSS-ресурса, ускоряя загрузку. Это достигается в первую очередь путем удаления содержимого из исходного CSS-файла, такого как пробелы и другие невидимые символы, и вывода результата в новый оптимизированный файл:
/* Unminified CSS: */
/* Heading 1 */
h1 {
font-size: 2em;
color: #000000;
}
/* Heading 2 */
h2 {
font-size: 1.5em;
color: #000000;
}
/* Minified CSS: */
h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}
В своей самой базовой форме минимизация CSS является эффективной оптимизацией, которая может улучшить FCP вашего сайта, а в некоторых случаях, возможно, даже LCP. Такие инструменты, как сборщики, могут автоматически выполнять эту оптимизацию для вас в производственных сборках.
Удалить неиспользуемый CSS
Перед рендерингом любого контента браузер должен загрузить и проанализировать все таблицы стилей. Время, необходимое для завершения парсинга, также включает стили, которые не используются на текущей странице. Если вы используете упаковщик, который объединяет все ресурсы CSS в один файл, ваши пользователи, вероятно, загружают больше CSS, чем необходимо для рендеринга текущей страницы.
Чтобы обнаружить неиспользуемый CSS для текущей страницы, используйте инструмент Coverage в Chrome DevTools.

Удаление неиспользуемого CSS имеет двойной эффект: помимо сокращения времени загрузки, вы оптимизируете построение дерева рендеринга , поскольку браузеру приходится обрабатывать меньше правил CSS.
Избегайте деклараций CSS @import
Хотя это может показаться удобным, следует избегать объявлений @import
в CSS:
/* Don't do this: */
@import url('style.css');
Аналогично тому, как элемент <link>
работает в HTML, объявление @import
в CSS позволяет импортировать внешний ресурс CSS из таблицы стилей. Главное различие между этими двумя подходами заключается в том, что элемент HTML <link>
является частью ответа HTML и, следовательно, обнаруживается гораздо раньше, чем файл CSS, загруженный объявлением @import
.
Причина этого в том, что для обнаружения объявления @import
необходимо сначала загрузить содержащий его файл CSS. Это приводит к так называемой цепочке запросов , которая — в случае CSS — задерживает время, необходимое для первоначального отображения страницы. Другим недостатком является то, что таблицы стилей, загруженные с использованием объявления @import
, не могут быть обнаружены сканером предварительной загрузки и, следовательно, становятся поздно обнаруженными блокирующими отображение ресурсами.
<!-- Do this instead: -->
<link rel="stylesheet" href="style.css">
В большинстве случаев вы можете заменить @import
с помощью элемента <link rel="stylesheet">
. Элементы <link>
позволяют загружать таблицы стилей одновременно и сокращают общее время загрузки, в отличие от объявлений @import
, которые загружают таблицы стилей последовательно .
Встроенный критический CSS
Время, необходимое для загрузки CSS-файлов, может увеличить FCP страницы. Встраивание критических стилей в документ <head>
устраняет сетевой запрос на CSS-ресурс и — при правильном выполнении — может улучшить начальное время загрузки, когда кэш браузера пользователя не подготовлен. Оставшийся CSS можно загрузить асинхронно или добавить в конец элемента <body>
.
<head>
<title>Page Title</title>
<!-- ... -->
<style>h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}</style>
</head>
<body>
<!-- Other page markup... -->
<link rel="stylesheet" href="non-critical.css">
</body>
С другой стороны, встраивание большого количества CSS добавляет больше байтов к первоначальному ответу HTML. Поскольку ресурсы HTML часто не могут кэшироваться очень долго — или вообще не могут — это означает, что встраиваемый CSS не кэшируется для последующих страниц, которые могут использовать тот же CSS во внешних таблицах стилей. Протестируйте и измерьте производительность вашей страницы, чтобы убедиться, что компромиссы стоят усилий.
CSS-демонстрации
JavaScript
JavaScript управляет большей частью интерактивности в Интернете, но это имеет свою цену. Слишком большое количество JavaScript может замедлить загрузку вашей веб-страницы и даже вызвать проблемы с отзывчивостью, которые замедляют взаимодействие — и то, и другое может раздражать пользователей.
JavaScript, блокирующий рендеринг
При загрузке элементов <script>
без атрибутов defer
или async
браузер блокирует парсинг и рендеринг до тех пор, пока скрипт не будет загружен, проанализирован и выполнен. Аналогично, встроенные скрипты блокируют парсер до тех пор, пока скрипт не будет проанализирован и выполнен.
async
против defer
async
и defer
позволяют внешним скриптам загружаться без блокировки HTML-парсера, в то время как скрипты (включая встроенные скрипты) с type="module"
откладываются автоматически. Однако async
и defer
имеют некоторые различия, которые важно понимать.
Скрипты, загруженные с помощью async
анализируются и выполняются немедленно после загрузки, тогда как скрипты, загруженные с помощью defer
выполняются после завершения анализа HTML-документа — это происходит одновременно с событием браузера DOMContentLoaded
. Кроме того, async
скрипты могут выполняться вне очереди, тогда как скрипты defer
выполняются в порядке, в котором они появляются в разметке.
Рендеринг на стороне клиента
В общем случае следует избегать использования JavaScript для рендеринга любого критического контента или элемента LCP страницы. Это известно как рендеринг на стороне клиента и является техникой, широко используемой в одностраничных приложениях (SPA).
Разметка, отрисованная JavaScript, обходит сканер предварительной загрузки, поскольку ресурсы, содержащиеся в разметке, отрисованной клиентом, не могут быть им обнаружены . Это может задержать загрузку важных ресурсов, таких как изображение LCP. Браузер начинает загрузку изображения LCP только после того, как скрипт будет выполнен и добавит элемент в DOM. В свою очередь, скрипт может быть выполнен только после того, как он будет обнаружен, загружен и проанализирован. Это известно как цепочка критических запросов , и его следует избегать.
Кроме того, рендеринг разметки с использованием JavaScript с большей вероятностью будет генерировать длительные задачи , чем разметка, загружаемая с сервера в ответ на запрос навигации. Широкое использование клиентского рендеринга HTML может негативно повлиять на задержку взаимодействия . Это особенно актуально в случаях, когда DOM страницы очень большой , что запускает значительную работу по рендерингу, когда JavaScript изменяет DOM.
Минификация
Подобно CSS, минификация JavaScript уменьшает размер файла ресурса скрипта. Это может привести к более быстрой загрузке, позволяя браузеру быстрее перейти к процессу парсинга и компиляции JavaScript.
Кроме того, минификация JavaScript идет на один шаг дальше, чем минификация других ресурсов, таких как CSS. Когда JavaScript минифицируется, он не только удаляет такие вещи, как пробелы, табуляции и комментарии, но и символы в исходном JavaScript сокращаются. Этот процесс иногда называют углификацией . Чтобы увидеть разницу, возьмите следующий исходный код JavaScript:
// Unuglified JavaScript source code:
export function injectScript () {
const scriptElement = document.createElement('script');
scriptElement.src = '/js/scripts.js';
scriptElement.type = 'module';
document.body.appendChild(scriptElement);
}
Если предыдущий исходный код JavaScript искажен, результат может выглядеть примерно так:
// Uglified JavaScript production code:
export function injectScript(){const t=document.createElement("script");t.src="/js/scripts.js",t.type="module",document.body.appendChild(t)}
В предыдущем фрагменте вы можете видеть, что читаемая человеком переменная scriptElement
в исходном коде сокращена до t
. При применении к большой коллекции скриптов экономия может быть весьма существенной, не влияя на функции, предоставляемые производственным JavaScript веб-сайта.
Если вы используете bundler для обработки исходного кода вашего веб-сайта, uglification часто выполняется автоматически для производственных сборок. Ugliifiers, например, Terser , также обладают высокой степенью настройки, что позволяет вам настраивать агрессивность алгоритма uglification для достижения максимальной экономии. Однако значения по умолчанию для любого инструмента uglification обычно достаточны для достижения правильного баланса между размером вывода и сохранением возможностей.
Демонстрации JavaScript
Проверьте свои знания
Как лучше всего загрузить несколько CSS-файлов в браузер?
@import
.<link>
.Что делает сканер предварительной загрузки браузера?
<link rel="preload">
в HTML-ресурсе.Почему браузер по умолчанию временно блокирует парсинг HTML при загрузке ресурсов JavaScript?
Далее: Помощь браузеру с подсказками по ресурсам
Теперь, когда вы разобрались с тем, как ресурсы, загруженные в элемент <head>
, могут влиять на начальную загрузку страницы и различные метрики, пора двигаться дальше. В следующем модуле изучаются подсказки ресурсов и то, как они могут давать браузеру ценные подсказки, чтобы начать загрузку ресурсов и открывать соединения с кросс-источниковыми серверами раньше, чем браузер мог бы это сделать без них.