Кирилл Мыльников

 Frontend разработчик ГК Юзтех

Всем привет, я Кирилл, frontend разработчик компании Usetech. Сегодня я бы хотел поговорить о том, как можно улучшить производительность сайта с помощью обычных CSS свойств и на что стоит обращать внимание. Но прежде чем приступим к улучшению производительности сайта, давайте поговорим о том, какие проблемы возникают с CSS:

  • CSS блокирует рендеринг: каждый <link> и @import останавливает другие загрузки, пока браузер загружает и анализирует требуемый файл CSS;

  • CSS влияет на рендеринг. Браузеры рендерят страницу в три этапа: (размер элементов), рисование (текст, цвета и т.д.) и позиционирование. Некоторые свойства CSS запускают все три фазы, что может сказаться на производительности;

  • Код CSS со временем может разрастаться. Выявление неиспользуемых стилей может быть сложной задачей, а их удаление может привести к хаосу, но разработчики выбирают лёгкий путь и добавляют больше свойств. Таблица стилей становится всё больше. Отсюда вывод: чем больше файл, тем дольше время загрузки и обработки.

Следующие советы помогут вам оптимизировать ваш CSS код.

Используйте инструменты анализа производительности CSS

Все браузеры предлагают панель DevTools, которая обычно открывается из меню «Дополнительные инструменты» или с помощью сочетаний клавиш Ctrl | Cmd + Shift + i или F12. Панель «Network» — отличное место для начала. После обновления она показывает диаграмму загрузок ресурсов.

Пример:

Более длинные полосы выделяют ресурсы с медленной загрузкой или блокировкой рендеринга (белые полосы).

Панель Lighthouse, доступная в Chrome, Edge, Opera и т.д, может оценивать показатели вашего сайта и предлагать рекомендации по производительности. Также браузеры предоставляют панель «sources», помогающую найти неиспользуемые CSS свойства, отмеченные красной рамкой.

Учтите несколько вещей:

  • Данный стиль может выглядеть неиспользуемым, потому что виджет не просматривается или не используется в данный момент;

  • Сбрасывается при обновлении или переходе на другую страницу

Большинство DevTools предлагают панель производительности. В основном она используется для оценки JavaScript, но может определить пики загрузки ЦП и макета при изменении CSS.

Топ онлайн инструментов которые подскажут вам об улучшении CSS:

  1. OpenReplay;

  2. Google PageSpeed;

  3. WebPageTest;

  4. Pingdom WebSite Speed Test.

Улучшаем перфоманс не касаясь кода CSS

Что необходимо:

  • Перейти на более удобный и быстрый хостинг или возможность использовать сеть CDN;

  • Активный HTTP/2 и выше;

  • Включить GZIP или лучшее сжатие;

  • Убедитесь, что ваши браузеры могут кэшировать ваш CSS, установите соответствующие заголовки HTTP, такие как Expires, Last-Modified.

Предварительно загрузите таблицы стилей

Тег <link rel="preload"/> позволяет запускать CSS загрузку до того, как на неё будет сделана ссылка. Это может быть удобно, когда ссылки на таблицы стилей идут после других ресурсов или у вас есть @import.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
< !-- preload CSS file -->
<link rel="preload" href="styles.css" as="style" />
  <!-- use preloaded styles -->
<link rel="stylesheet" href="styles.css" />

Главное - не злоупотребляйте предзагрузкой. Если загружать всё подряд, то ваш сайт не станет быстрее волшебным образом, а наоборот.

Удалите неиспользуемые стили и файлы

Удалите все файлы и стили, которые не используете. Но эта задача не так проста. Возможно у вас уже есть большой проект и вам нужно просмотреть все страницы и выявить неиспользуемые CSS стили. Для этой задачи есть несколько инструментов, которые помогут выявить избыточный код:

Используйте шрифт ОС

Использование шрифтов ОС поможет сэкономить сотни килобайт и избежать таких проблем, как мигание нестилизованного или просто текста.

Удалите ненужные шрифты

Стандартные шрифты требуют отдельных файлов для каждого веса и стиля. Вы можете удалить те, которые редко используются, а также некоторые символы и глифы шрифта.

Размещайте файлы шрифтов локально

Ссылаться на шрифты Google несложно, но дополнительные запросы, создание подборов и отслеживание использования могут сказаться на производительности. Локальные шрифты загружаются и отображаются значительно быстрее.

Web Open Font Format 2.0(WOFF2) — единственная версия файла, которая вам нужна. Поддерживается всеми браузерами, в том числе новыми. Вы также должны определить соответствующий font-display параметр загрузки в вашем CSS.

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

  • swap  — использует первый резервный шрифт OC, пока шрифт не станет доступным. Текст всегда читается, но мигание нестилизованного текста может раздражать, если два набора символов разного размера.

  • fallback  — компромисс между FOIT и FOUT. Текст невидим около 100 мс. Затем используется веб‑шрифт, если он доступен. Если нет, он возвращается к swap

  • optional — тот же, что и fallback и после загрузки веб‑шрифта, но замена шрифта не происходит. Он появляется при загрузке следующей страницы.

Используйте HTML <link/> вместо CSS @import

Это позволяет разделить большой файл CSS на более мелкие, но каждый @import блокирует рендеринг. Браузер должен анализировать и загружать по очереди каждый файл.

@import url("main.css");
@import url("base.css");
@import url("footer.css");

Использование <link/> тегов HTML более эффективно, потому что каждая таблица стилей будет загружаться параллельно.

<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="footer.css">

Объединяйте и минимизируйте свои таблицы стилей

HTTP/2 лучше обслуживает несколько таблиц стилей, чем HTTP/1. Но для одного файла требуется один заголовок и его можно сжимать и кэшировать более эффективно. Вы можете использовать любое количество файлов во время разработки, но используйте шаг сборки для объединения и минимизации в единую таблицу стилей. Такие инструменты как препроцессор SASS и PostCSS, помогут вам в этом случае.

Используйте современные CSS Layouts

Старые методы расположения элементов на странице, например, float или table, сложны в управлении и требуют большего количества кода для управления интервалами и медиа‑запросами. Если они есть в вашем проекте, то пришло время переключиться на:

Все они проще в разработке, используют меньше кода, быстрее рендерятся и могут адаптироваться к размерам экрана.

Замените изображения CSS эффектами

По возможности используйте код CSS для создания графики вместо ссылки на изображения. Современные браузеры поддерживают градиенты, узорчатые границы, закругленные углы, тени, фильтры, режимы наложения, маски и т. д.

Никогда не используйте растровые изображения в кодировке base64

Вы можете встроить изображения в CSS, используя кодировку в base64, которые преобразуют пиксели в текстовые символы.

.img {
  background-image: url('data:image/jpg;base64,0123456...');
}

Этот метод приводит к меньшему количеству HTTP запросов, но может снизить производительность CSS.

  • Строки base64 обычно на 30% больше, чем двоичные данные;

  • Браузерам требуется дополнительный шаг для декодирования строки и изменения одного пикселя. При этом будет необходимо загрузить CSS файл повторно.

Используйте данные свойства в редких случаях, только когда это реально необходимо.

Стилизуйте SVG с помощью CSS

Часто SVG код вставляют прямо в HTML. Покажу на примере:

<main>
  <svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 120 120">
    <circle cx="60" cy="60" r="50" stroke-width="3" stroke="#00f" fill="#ff0" />
  <svg>
</main>

Вы можете:

  • Удалить атрибут стиля SVG из вашего HTML;

  • Использовать одно и то же изображение с разными стилями для разных разделов или страниц;

  • Анимировать.

Пример:

circle {
  stroke-width: 10px;
  stroke: #000;
  fill: #0f0;
}

Будьте осторожны с CSS фреймворками

CSS фреймворки могут быть полезными, когда начинаете веб‑разработку. Они предоставляют набор привлекательных, готовых стилей.

  • Фреймворки могут содержать значительный объем кода, но вы, вероятно, используете лишь небольшую часть стилей. По возможности убедитесь, что вы используете только те функции, которые вам нужны;

  • Может быть сложно переопределить стили фреймворка, когда они представляют собой не совсем то, что вам нужно. По итогу выйдет, что вы используете два набора стилей, а нужно было один.

Упростите свои селекторы

У современных браузеров нет проблем с разбором длинных селекторов, но уменьшение сложности позволит уменьшить размер файлов, повысить производительность и упростить поддержку вашего кода.

А ещё следите за новыми селекторами, которые помогут преобразовать селекторы следующим образом:

article section.primary:not(:first-child) h1,
article section.primary:not(:first-child) h2,
article section.primary:not(:first-child) p,
article section.secondary:not(:first-child) h1,
article section.secondary:not(:first-child) h2,
article section.secondary:not(:first-child) p {
  color: red;
} 

Такой же вариант, но только с новым селектором:

article section:not(:first-child):where(.primary, .secondary) :where(h1, h2, p) {
  color: red;
}

Используйте CSS-переходы и анимацию

Переходы и анимация на CSS будут более плавными, чем эффекты на основе JavaScript, которые изменяют те же свойства. Однако лучше избегать анимации свойств, вызывающих изменения размеров элементов (width, height, padding, border) или положение (top, bottom, left, right, margin). Это может привести к перерисовке всей страницы в каждом кадре анимации.

Эффектные свойства анимации включают в себя:

  • opacity: прозрачность;

  • filter: размытие, контраст и т.д;

  • transform: перемещение, поворот и т. д.

Используйте will-change при необходимости

Свойство will-change позволяет заранее сообщить браузеру об изменениях (анимациях, перемещениях) которые могут произойти с элементом. Так браузер успеет оптимизировать выполнение этих изменений, до того как это произойдет.

Чтобы включить will-change, нужно добавить следующее правило для элемента, который будет изменяться. Пример:

div {
  will-change: /* значение */;
}

Значения will-change:

  • auto — не указывает никакого конкретного свойства, браузер будет работать как обычно. Значение по умолчанию.

  • scroll-position — указывает, что ожидается изменение положения элемента при прокрутке страницы. Браузер заранее готовится и отрисовывает содержимое за пределами видимой части страницы.

  • contents — указывает, что ожидается изменение содержимого элемента. Браузер ограничивает или полностью прекращает кэширование элемента и отрисовывает его с нуля при каждом изменении его содержимого.

Пример:

div {
  will-change: transform;
  will-change: opacity;
  will-change: top, left, bottom, right;
}

Примечание:

  • Не стоит использовать will-change для большого числа элементов. Браузер и так пытается максимально оптимизировать работу страницы. Если заставить его отслеживать все возможные изменения элементов, это вызовет больший расход ресурсов и может замедлить работу.

  • will-change — используется как крайнее средство, если есть видимые задержки в работе сайта;

  • will-change — необходимо включать до изменения состояния элемента. Если применить свойство в процессе изменения состояния, оно не успеет сработать и не даст никакого эффекта;

  • will-change лучше назначать через JS, так как можно его отключать после завершения изменения элемента.

Такой код не сработает:

/* Изменение элемента происходит при наведении курсора */
.element:hover {
  /* нужно подготовиться к изменению, которое уже происходит, код не сработает */
  will-change: transform;
  transition: transform 2s;
  transform: rotate(30deg) scale(1.5);
}

Если изменение происходит при наведении курсора на элемент, то можно включать will-change при наведении на родительский элемент.

Пример:

.element {
  transition: opacity 0.3s linear;
}
/* включаем will-change для элемента, когда мышка наводится на его родительский элемент */
.parent:hover .element {
  will-change: opacity;
}
/* применение изменения, когда мышка наведена на элемент */
.element:hover {
  opacity: 0.5;
}

Обработка HTTP заголовка Save-Data

Заголовок HTTP Save‑Data указывает, что пользователь запросил сокращенные данные. Браузеры могут отмечать эту опцию как «lite» или «turbo» режим. Когда она включена, Save‑Data заголовок отправляется с каждым запросом браузера.

Пример:

GET /main.css HTTP/2.0
Host: mysite.com
Save-Data: on

Сервер может обнаружить заголовок и ответить соответствующим образом. Например, он может обслужить простой файл CSS с линейным макетом, использующим шрифт ОС, блочные цвета и т. д.

Клиентский JavaScript тоже может обнаружить эту Save‑Data опцию. Следующий код добавляет класс к <html> элементу, когда Save‑Data не включен.

Пример:

if ('connection' in navigator && !navigator.connection.saveData) {
  document.documentElement.classList.add('fullUX');
}

Можно применять таблицу стилей без какого-либо взаимодействия с сервером. Пример:

/* no hero image by default */
header {
  background-color: #ceb;
  background-image: none;
}

/* hero image when Save-Data is not enabled */
.fullUX header {
  background-image: url('bigimg.jpg');
}

Медиа‑запрос prefers-reduced-data предлагает альтернативу только на CSS, но пока не поддерживается ни в одном браузере. Пример:

header {
  background-color: #ceb;
  background-image: none;
}


@media (prefers-reduced-data: no-preference) {
  header {
    background-image: url('bigimg.jpg');
  }
}

Рассмотрите критический встроенный CSS

Есть некоторые инструменты, например, Lighthouse, который может порекомендовать вам «встроить критический CSS».

  • Определение основных стилей, используемых элементами в верхней части страницы, которые видны при загрузке страницы;

  • Загрузка оставшегося CSS асинхронно, чтобы избежать блокировки рендеринга.

В следующем примере оставшийся CSS загружается в виде «print» таблицы стилей, которую браузер загружает асинхронно с более низким приоритетом. Код onload переключает его обратно на стандартную таблицу стилей для всех носителей после его загрузки. Гарантирует <noscript>, что он ещё загружается, если JavaScript не включён. Пример:

<head>
<!-- critical styles -->
<style>
  body { font-family: sans-serif; color: #111; }
</style>
<!-- load remaining styles -->
<link rel="stylesheet" href="main.css" media="print" onload="this.media='all'">
<noscript>
  <link rel="stylesheet" href="main.css">
</noscript>
<head>

Этот метод заметно повышает производительность и может нести пользу сайтам или одностраничным приложениям.

А ещё он приносит пользу только при загрузке первой страницы пользователя. Последующие загрузки страниц могут использовать кэшированную таблицу стилей, поэтому встроенный CSS не нужен и снижает производительность.

Создайте таблицы стилей для устройств

Единая (встроенная) таблица стилей, содержащая код для всех устройств, удобна для большинства сайтов. Однако если ваша кодовая база велика или дизайн мобильных и десктопных приложений значительно различается, вы можете создать таблицы стилей для конкретных устройств. Пример:

<!-- core styles for all devices -->
<link rel="stylesheet" href="core.css">
<!-- served to screens less than 400px wide -->
<link rel="stylesheet" media="(max-width: 399px)" href="mobile.css">
<!-- served to screens 400px or wider -->
<link rel="stylesheet" media="(min-width: 400px)" href="desktop.css">

Contain CSS

Сдерживание CSS повышает производительность, позволяя вам идентифицировать изолированные страницы. Затем браузер может оптимизировать процесс рендеринга определённых блоков содержимого DOM дерева.

Свойство containподдерживает одно и несколько следующих значений в списке, разделенном пробелами:

  • none: нет сдерживания (по умолчанию);

  • layout: изолировать элемент от остальной части страницы: его содержимое не повлияет на расположение других элементов;

  • paint: обрезать элемент до определённого размера без видимого переполнения;

  • size: встроенные и блочные размеры элемента не зависят от содержимого — нет необходимости вычислить размер дочерних элементов;

  • inline-size: аналогично size, но применяется только к встроенным размерам.

Ещё доступны два конкретных значения:

  • strict: применить все правила сдерживания, кроме none;

  • content: применить layout и paint.

Рассмотрим такой пример: есть большой список ul, на нем установлен contain: strict;. При изменении содержимого любого дочернего li элемента браузера он не будет пересчитывать размер или положение этого элемента, других элементов в списке или любых элементов на странице. Рендеринг быстрее.

Прогрессивный рендеринг

Прогрессивный рендеринг — это метод, который определяет отдельные таблицы стилей для каждой страницы и компонента. Это принесёт пользу крупным сайтам со значительным количеством CSS, где страницы имеют разный дизайн или состоят из ряда компонентов.

  • Нет необходимости загружать одну большую таблицу стилей на первой странице загрузки, содержащую CSS для компонентов, которые вы не используете;

  • Изменение стилей одного компонента не влияет на другие кэшированные файлы.

Вы можете использовать нативные веб‑компоненты или ссылаться на меньшие файлы CSS непосредственно перед тем, как компонент появится в вашем HTML. Пример:

<body>
  <!-- core styles -->
  <link rel="stylesheet" href="core.css" />
  <!-- header -->
  <link rel="stylesheet" href="header.css" />
  <header>header content</header>
  <!-- article -->
  <link rel="stylesheet" href="article.css" />
  <main>
    <h1>title</h1>
    <!-- widget -->
    <link rel="stylesheet" href="widget.css" />
    <div class="widget">widget content</div>
  </main>
  <!-- footer -->
  <link rel="stylesheet" href="footer.css" />
  <footer>footer content</footer>
</body>

Большинство браузеров отображают HTML во время его загрузки. Каждая таблица стилей <link> блокирует рендеринг, но размер каждого файла не должен превышать несколько килобайт.

Старые браузеры могут показывать пустую страницу до тех пор, пока не загрузится весь CSS, но общий эффект не должен быть хуже, чем одна большая таблица стилей, блокирующая рендеринг.

Используйте веб-компоненты

Собственные веб-компоненты браузера позволяют создавать инкапсулированные настраиваемые функции с единой ответственностью. Другими словами, вы можете создавать HTML-теги, которые совместимы с любыми фреймворками.

Фреймворки JavaScript представили концепции, но их компоненты никогда не были по-настоящему отдельны от других CSS или JavaScript. Нативные компоненты предоставляют Shadow DOM, который изолирует элемент, поэтому стили и функциональность не могут просачиваться внутрь или наружу. Преимущества:

  • По умолчанию CSS компонента отвечает за его стиль. Он загружается и кэшируется только тогда, когда этот компонент используется;

  • Компонентный CSS может быть более лаконичным, чем страничный CSS, потому что ему не нужны сложные или специфические для размещения селекторы.

Компоненты могут отображать теневые :part элементы, поэтому возможны ограниченные внешние стили.

Используйте новые и передовые методы разработки

Методы передовой практики развиваются, устаревают и различаются от разработчика к разработчику, но надежные подходы включают в себя следующие пункты:

  • Организуйте свой CSS в файлы меньшего размера с отдельными функциями, например, верхний и нижний колонтитулы, элементы формы, таблицы и меню;

  • Автоматизируйте процесс сборки для создания единой таблицы стилей и автоматического обновления с помощью такого инструмента, как Browsersync;

  • Документируйте свой код; 

  • Используйте инструменты linting и браузерные DevTools, чтобы убедиться, что вы установили допустимые свойства и значения;

  • Реализовывайте переиспользуемые компоненты, стили и т.д.

Заключение

Мы с вами рассмотрели все виды оптимизации через CSS. Также вы можете дополнить в комментариях, если что-то я упустил или знаете какой-то иной способ оптимизации. В целом CSS легко изучить, но сложно освоить. Твёрдое понимание основ CSS может революционизировать наш рабочий процесс, улучшить ваши приложения и заметно повысить производительность.

Комментарии (10)


  1. altocodes
    00.00.0000 00:00
    -1

    Возможно очевидно, но как-то был проект с четырями <canvas> на странице. Почему-то именно мой macbook m1 тормозил на нем. Долго ловили, как убрали, сразу стало все ок.


  1. VolumesOne88
    00.00.0000 00:00
    +1

    Спасибо за интересную статью! Очень много полезных техник!


    1. UseTech Автор
      00.00.0000 00:00

      Вам спасибо за обратную связь!


  1. savostin
    00.00.0000 00:00

    Локальные шрифты загружаются и отображаются значительно быстрее.

    Хотелось бы доказательств каких-то. Имхо шрифт (да и любой другой файл, который используется широко на многих сайтах) может быть уже в кэше пользователя и не загружаться вообще. Мне кажется именно в этом их преимущество.


    1. alexxxnf
      00.00.0000 00:00
      +2

      Про то, что шрифт случайно окажется в кэше, уже можно забыть из-за такой штуки как cache partitioning. Чтобы избежать трекинга через ресурсы, теперь для каждого сайта в браузере свой кэш. По-моему, эта фича уже везде включена по умолчанию.


      1. savostin
        00.00.0000 00:00

        Хм, а зачем тогда все эти cdnjs и прочие похожие сервисы?


        1. Revertis
          00.00.0000 00:00

          Ну вот теперь только для удобства разработчика, но не для ускорения загрузки сайта. Устарело.


          1. alexxxnf
            00.00.0000 00:00

            Не всё так плохо. Раздача контента с сервера, который георафически ближе к клиенту, всё ещё работает.


  1. Yuri2112
    00.00.0000 00:00
    +2

    Интересно, спасибо автору!


  1. bubn0ff
    00.00.0000 00:00

    Автору спасибо, ждём новых интересных постов!