От автора: я все время думала, что вопрос, когда и как загружать CSS-файлы, лучше оставить самому браузеру. Браузеры спроектированы для работы с такими вещами. Зачем разработчикам что-то делать, если можно просто добавить ссылку с rel=»stylesheet» и href=»style.css» в head документа и забыть?
По-видимому, сегодня этого уже не достаточно, по крайней мере, если вы думаете о скорости сайта и быстром рендеринге страниц. Учитывая, как эти два фактора влияют на UX и успех сайта, скорее всего, вы уже искали способы контролировать метод загрузки стилей в браузере по умолчанию.
В этой статье я расскажу о том, что может пойти не так, когда браузеры загружают CSS-файлы, а также мы обсудим возможные подходы к проблеме, которые вы испытаете на своем сайте.
Проблема: CSS блокирует рендеринг
Если вы хоть раз пользовались Google Page Speed Insights для проверки производительности сайта, вы должны были встречать сообщения подобного содержания:
Google Page Insights описывает проблему и предлагает стратегию ее решения.
Сначала давайте попробуем лучше понять суть проблемы.
Для отображения веб-страниц браузеры используют DOM (Document Object Model) и CSSOM (CSS Object Model). DOM – модель HTML, необходимая браузеру для рендеринга структуры страницы и ее контента. CSSOM – карта CSS, которую использует браузер для стилизации веб-страниц.
CSS является частью критического пути рендеринга, т.е. браузер тормозит рендеринг веб-страницы до тех пор, пока не загрузятся и обработаются все HTML и CSS файлы. Это объясняет, почему HTML и CSS файлы считаются блокирующими рендеринг файлами. Внешние стили включают в себя многократные запросы туда-обратно между браузером и сервером. Это может вызывать задержку между временем загрузки HTML и временем рендеринга страницы на экран.
Проблема заключается в этой задержке, когда пользователь смотрит на пустой экран несколько миллисекунд. Не самый лучший опыт от первого посещения сайта.
Концепция критического CSS
С одной стороны, HTML в первую очередь влияет на рендеринг страницы, но в противном случае вообще нечего было бы рендерить. Можно ли то же самое сказать про CSS?
Конечно, страница без стилей совсем не user friendly, а значит, браузеру нужно подождать загрузки и парсинга CSS перед рендерингом страницы. С другой стороны, можно сказать, что не все стили критичны для построения нормальной страницы. В первую очередь пользователей интересует верхняя часть страницы, видимая на экране после загрузки без прокрутки.
За этой мыслью кроется доминирующий на сегодня подход по решению нашей проблемы и предложения из сообщения Google Page Insights. Что нас интересует: «Попробуйте отложить загрузку блокирующих ресурсов или делайте это асинхронно, или же встройте критические части ресурсов напрямую в HTML.»
Но как отложить загрузку стилей на странице или сделать ее асинхронной?
Все не так просто, как может звучать. Здесь не получится добавить атрибут toss к тегу link, как это можно было в script.
Ниже описано несколько методов, которыми разработчики решают эту задачу.
Используйте медиа типы и медиа запросы для минимизации блокировок рендеринга
Ilya Grigorik иллюстрирует простой способ минимизации блокировок рендеринга страницы, который делится на два этапа:
необходимо разбить внешний CSS на несколько файлов по медиа типам и медиа запросам, тем самым избегая загрузки больших CSS-документов;
необходимо ссылаться на все файлы с подходящим медиа типом и медиа запросом внутри тега link. Это предотвращает блокировку некоторых файлов стилей, если не соблюдаются условия медиа типа и медиа запроса.
Как пример, ссылки на внешние CSS-файлы в head документа могут выглядеть так:
1 2 3 |
<link href="style.css" rel="stylesheet"> <link href="print.css" rel="stylesheet" media="print"> <link href="other.css" rel="stylesheet" media="(min-width: 40em)"> |
Первая ссылка в коде сверху не использует медиа тип и медиа запрос. Браузер считает, что она удовлетворяет всем кейсам, поэтому она всегда блокирует рендеринг страницы на время загрузки и обработки.
У второй ссылки есть атрибут media=»print», который говорит браузеру: «привет, браузер, смотри, чтобы этот файл стилей применялся только, если страница уходит на печать». При обычном просмотре страницы на экране браузер понимает, что этот файл стилей ему не нужен, и рендеринг не блокируется.
В третьей ссылке есть media=»(min-width: 40em)», что значит, что стили в ссылке блокируют рендеринг только, если соблюдено условие медиа запроса. То есть когда ширина вьюпорта равна минимум 40em. Во всех остальных случаях ресурс не блокирует рендеринг.
Также учтите, что браузер всегда будет загружать все стили, но не блокирующим стилям он будет отдавать более низкий приоритет.
Вывод: подход не полностью устраняет проблему, но может значительно минимизировать время блокировок рендеринга страниц.
Используйте директиву preload на теге link
Самый новый способ противостояния блокировкам страниц в браузере при загрузке внешних стилей – стандартизированная директива preload. С помощью preload мы-разработчики можем сказать браузерам загружать определенные ресурсы, потому что мы знаем, что они понадобятся в скором времени.
Scott Jehl, дизайнер и разработчик из The Filament Group, первый придумал этот способ. Что нужно делать. Добавьте preload в ссылку стилей и вставьте немного JS-магии с помощью события onload. Результат – асинхронная загрузка стилей прямо в разметку:
1 |
<link rel="preload" as="style" href="style.css" onload="this.rel='stylesheet'"> |
Код выше дает браузеру возможность скачать style.css, не блокируя рендеринг. Как только ресурс загрузится, т.е. сработает событие onload, скрипт заменит значение preload атрибута rel на stylesheet и применит стили на странице.
В настоящее время поддержка preload ограничена Chrome и Opera. Но не бойтесь, Filament Group разработали полифил. Более подробно в следующей секции.
Решение от Filament Group: от инлайнового CSS к HTTP/2 Server Push
Filament Group на протяжении долгого времени отдавали решению проблемы блокирования рендеринга из-за загрузки критических ресурсов наивысший приоритет. Filament Group – отмеченное наградами агентство по дизайну и разработке из Бостона: «Наш главный приоритет – работа с HTML, готовым для рендеринга сразу после загрузки в браузере.»
Последнее обновление по оптимизации загрузки страниц подчеркнуло их прогресс от инлайнового критического CSS до суперэффективного HTTP/2 Server Push. Общая стратегия:
любой критический для рендеринга первой видимой части страницы файл должен быть включен в HTML-ответ от сервера;
любой некритичный для рендеринга первой видимой части страницы файл должен загружаться асинхронно без блокировки рендеринга
До HTTP/2 Server Push была первая версия метода. В первой версии нужно было выбрать критические CSS-правила, т.е. те правила, которые нужны для плавного рендеринга верхней видимой части страницы и стили по умолчанию, после чего вставить их в тег style внутрь head на странице. Разработчики Filament Group написали Critical, крутой JS-плагин, автоматизирующий задачу извлечения критического CSS.
С принятием HTTP/2 Server Push Filament Group стали работать не только над инлайновыми критическими стилями.
Что такое Server Push? Для чего он нужен?
«Server push позволяет посылать пользователю файлы с сайта еще до того, как он их попросит. …Server push позволяет серверу превентивно «толкать» файлы сайта в клиент без явного запроса со стороны пользователя. При правильном подходе мы можем посылать пользователю файлы, которые, как мы точно знаем, ему понадобятся для запрошенной страницы. Jeremy Wagner»
Другими словами, когда браузер запрашивает определенную страницу, например, index.html, все файлы, от которых зависит эта страница, будут включены в ответ (style.css, main.js и т.д.), давая огромное ускорение при рендеринге страницы.
Разберем на практике способ №2 (асинхронную загрузку некритического CSS). Здесь используется комбинация preload из предыдущей техники и LoadCSS, JS-библиотеки от Filament Group, которая включает полифил для браузеров без поддержки preload.
Для работы с LoadCSS необходимо включить этот код в head:
1 2 3 4 |
<link rel="preload" href="mystylesheet.css" as="style" onload="this.rel='stylesheet'"> <!-- for browsers without JavaScript enabled --> <noscript><link rel="stylesheet" href="mystylesheet.css"> </noscript> |
Обратите внимание на noscript. Внутри этого тега подключается файл стилей, как обычно. Этот блок обрабатывает ситуации, когда JS не сработал или был отключен.
Под noscript подключите LoadCSS и LoadCSS rel=preload polyfill script в теге script:
1 2 3 4 5 6 |
<script> /*! loadCSS. [c]2017 Filament Group, Inc. MIT License */ (function(){ ... }()); /*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */ (function(){ ... }()); </script> |
Детальную инструкцию по использованию LoadCSS можно найти на странице репозитория проекта на GitHub, а увидеть вживую в демо.
Я изучал этот подход: в нем используется техника прогрессивного улучшения, и он чистый и понятный для разработчиков.
Разместите ссылку на файлы стилей внутри body документа
По словам Jake Archibald у предыдущего метода есть минус – он основывается на главном предположении, что вы или я считаем критическим CSS. Очевидно, что все стили, применяемые к верхней видимой части страницы, критичны. Тем не менее, мы почти точно можем определить первую видимую часть страницы после загрузки. «Почти» потому, что при изменении размеров вьюпорта, изменяется объем контента, видимый пользователю без прокрутки страницы. Поэтому «один размер подо все» не всегда надежно.
Jake Archibald предлагает другое решение:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<head> </head> <body> <!-- HTTP/2 push this resource, or inline it --> <link rel="stylesheet" href="/site-header.css"> <header>…</header> <link rel="stylesheet" href="/article.css"> <main>…</main> <link rel="stylesheet" href="/comment.css"> <section class="comments">…</section> <link rel="stylesheet" href="/about-me.css"> <section class="about-me">…</section> <link rel="stylesheet" href="/site-footer.css"> <footer>…</footer> </body> |
Как видите, ссылки на внешние стили размещены не в
, а внутри body документа.Более того, стили, отображаемые сразу на странице, будь-то инлайновые или HTTP/2 Server Push – это стили, применяемые к верхней части страницы. Все остальные внешние стили помещаются прямо перед HTML-контентом, к которому они применяются: article.css перед main, site-footer.css перед footer и т.д.
Чего пытается достичь данная техника: «План – для каждого link rel=”stylesheet” блокировать рендеринг последующего контента на время загрузки стилей, но разрешать рендеринг контента перед этим тегом. Стили загружаются параллельно, но применяются последовательно. … Так вы получаете страницу с последовательным рендерингом. Вам не нужно вычислять «верхнюю часть страницы», просто подключите CSS для компонента страницы прямо перед первым появлением этого компонента.»
Все браузеры разрешают вставлять тег link внутри body, но не все браузеры ведут себя одинаково. Тем не менее, в большинстве браузеров эта техника решает поставленную задачу.
Все детали можно узнать в посте Jake Archibald, а результат посмотреть в демо.
Заключение
Проблема с тем, как браузеры загружают стили, заключается в том, что рендеринг страницы блокируется до тех пор, пока не будут загружены все файлы. Таким образом, при первом посещении сайта страдает UX, особенно у людей со слабым соединением.
В этой статье я показала несколько подходов решения этой проблемы. Не думаю, что маленькие сайты с относительно небольшим структурированным CSS будут сильно страдать, если оставят загрузку стилей в браузере самому устройству.
А что вы думаете, улучшает ли ленивая загрузка стилей или их последовательное применение (подход Jake Archibald) скорость рендеринга сайта? Какая техника самая эффективная? Пишите в комментариях.
Редакция: Maria Antonietta Perna
Источник: //www.sitepoint.com/
Редакция: Команда webformyself.