Чего мне никогда не говорили о CSS

Автор оригинала: Charlie Gerard
  • Перевод

Фото Джантин Дурнбос на Unsplash

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

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

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

Этот список определённо не исчерпывающий. Он содержит только новое, что я узнала за последние несколько дней и чем хочу поделиться, вдруг это поможет кому-то ещё.

Терминология


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

Комбинатор потомков


Видели пробел между селекторами в вашем стиле? На самом деле у него есть название, это комбинатор потомков (descendant combinator).


Комбинатор потомков

Макет, отрисовка и композиция


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

1. Макет (layout)


Шаг макетирования — это расчёт, сколько места элемент занимает на экране. Изменение свойства 'layout' в CSS (например, ширина, высота) означает, что браузеру придётся проверить все остальные элементы и перерисовать страницу, то есть перекрасить затронутые области и наложить их друг на друга.

2. Отрисовка (paint)


Этот процесс заполняет пикселями все визуальные части элементов (цвета, границы и т. д.). Отрисовка элементов обычно выполняется на нескольких слоях.

Изменение свойства 'paint' не влияет на макет, поэтому браузер пропускает шаг макетирования, но всё равно делает отрисовку.

На отрисовку зачастую уходит больше всего времени при рендеринге.

3. Композиция (composite)


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

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

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

Производительность CSS


Селектор потомков может дорого обойтись


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

Например:


Пример селектора потомков

Браузеру придётся оценить все ссылки на странице, прежде чем перейти к тем, которые находятся внутри нашего раздела #nav.

Более эффективный способ — добавить конкретный селектор .navigation-link на каждую ссылку <a> внутри раздела #nav.

Браузер считывает селекторы справа налево


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

Рассмотрим следующий пример:


Браузер читает справа налево

Шаги:

  • сопоставить все <a> на странице;
  • найти все <a>, содержащиеся в <li>;
  • взять совпадения и сузить их до тех, которые содержатся в <ul>;
  • наконец, отфильтровать вышеупомянутый выбор до тех, которые содержатся в элементе с классом .container.

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

Чтобы улучшить производительность приведённого примера, мы могли бы заменить .container ul li a, добавив что-то вроде .container-link-style на самом теге <a>.

По возможности не изменяйте макет


Изменения некоторых свойств CSS требуют обновления всего макета.

Например, геометрические свойства width, height, top, left требуют заново рассчитать макет и обновить дерево рендеринга.

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

Будьте осторожны со сложностью отрисовки


Некоторые свойства CSS (например, размытие) обходятся дороже других, когда дело доходит до отрисовки. Подумайте о более эффективных способах достижения того же результата.

Дорогие свойства CSS


Некоторые свойства CSS дороже других. Это означает, что отрисовка происходит дольше.

Некоторые из дорогих свойств:

  • border-radius
  • box-shadow
  • filter
  • :nth-child
  • position: fixed

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

Порядок (ordering)


Порядок имеет значение


Посмотрим на такой CSS:



А затем посмотрим на HTML-код:



Порядок селекторов в HTML не имеет значения, а в CSS — имеет.



Хороший способ оценить производительность CSS — использовать инструменты разработчика в браузере.

В Chrome или Firefox можете открыть инструменты разработчика, перейти на вкладку «Производительность» и записать, что происходит при загрузке или взаимодействии с вашей страницей.


Скриншот вкладки «Производительность» в Chrome

Ресурсы


Исследуя тему для этой статьи, я наткнулась на некоторые действительно интересные инструменты, перечисленные ниже:

CSS Triggers — веб-сайт, перечисляющий некоторые свойства CSS и как они влияют на производительность.

Uncss — инструмент для удаления неиспользуемых стилей из CSS.

Css-explain — небольшой инструмент, объясняющий селекторы CSS.

Fastdom — инструмент пакетных операций чтения/записи DOM для ускорения производительности макета.

Пока всё! Надеюсь, это имеет смысл!
Поддержать автора
Поделиться публикацией

Похожие публикации

Комментарии 36

    +3
    С какого перепугу css язык программирования?
      0
      Думаю, что его можно считать декларативным DSL, хотя конечно это не очень принято.

      Но статья, конечно, заставила задуматься что это немного сложнее, чем кажется.
        +7
        Неожиданно, CSS является тьюринг-полным.
        Другой вопрос, что неизвестно что лучше: кодить задачи на брейнфаке или на css.
        Другой-другой вопрос, что CSS воспринимается и используется как декларативная шестеренка, поэтому обзывание его «языком программирования», это как называть микроскоп молотком.
          +1
          Если речь идет об этой реализации правила 110, то она требует действий пользователя: постоянного нажатия Tab + space (сначала нужно накликать желаемое начальное состояние в первой строке, потом следовать инструкции). С таким же успехом Тьюринг-полными можно назвать комментарии на Хабре и камни на берегу моря. Мне кажется, это не совсем честная Тьюринг-полнота, т.к. требует активного участия человека в вычислениях, по крайней мере для меня как любителя портировать трехмерные движки на все подряд.
            0
            Оно не требует осмысленного участия человека и не требует принятия решений. Норм, ИМХО.
          +1
          Какой лучший перевод, для computer language?
            0

            "Как и в любом компьютерном языке, для описания понятий...."


            "Как и в любом языке компьютера, для описания понятий...."


            я за второй вариант

            +7
            > Он содержит только новое, что я узнала за последние несколько дней и чем хочу поделиться, вдруг это поможет кому-то ещё.

            Это конечно неожиданно, потому что список достаточно большой, в нем много совершенно банальных вещей, которые не знать стыдно, и она google developer expert по web разработке.
              +5
              Совершенно не удивительно, что с такими google developer expert новый gmail теперь две и более минут сохраняет статус «прочитано» у прочитанного письма.
                0
                Google developer expert — это не должность в гугл. Автор оригинальной статьи работает в ThoughtWorks.
                0
                Про справа налево могут начинающего верстальщика на собеседовании спросить в обычной конторе, а тут — гугл. А уж то, что порядок имеет значение, трудно не заметить.

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

                Чтобы исправить это, я провела некоторые исследования и составила небольшой список


                Странная логика. Почему кто-то ей должен был об этом говорить?
                +2
                > Селектор потомков может дорого обойтись

                Хорошо бы показывать доказательную ссылку на исследования, а не просто «мне так сказали». По логике селекторов, ничто не мешает анализировать теги «a» только внутри области старшего селектора и этим могут различаться различные движки.
                  +2
                  Есть классическое описание процесса авторства Дэвида Бэрона: «How browser rendering works».
                  tl;dr: браузер во время применения стилей обрабатывает DOM-ноды и проверяет каждую ноду на соответствие селектору. Поэтому логично проверять сначала на соответствие правой части селектора, а зачем по необходимости проверить предыдущие и родительские.

                  Именно поэтому нет селектора, зависящего от следующего соседа, или :has() (в селекторах 4 уровня работает только в querySelector(All)) — слишком усложнит и замедлит процесс. Это особенно важно для анимаций, где желательно быстро обновлять необходимые значение. Вот, например, доклад Дэвида про анимации: vimeo.com/103108124
                    +1
                    В Яндексе проводили много исследований по этому делу, и расказывали много раз на Субботниках, и было это 6-8 лет назад (в общем давно).
                    С тех пор многое что изменилось, в том числе в Firefox(а вот это было уже недавно) прилетел новый CSS парсер, который парсит по-человечески.

                    Век учись, век переучивайся.
                      0
                      Не совсем понятно что значит «по-человечески» с алгоритмической точки зрения. Я думаю можно завести какие-то эвристики, которые на достаточно ранних этапах будут отсекать некоторые селекторы, для некоторых веток DOM. Но как в целом поменять механизм проверки селектора от потомка к родителю совершенно не представляю. Единственный вариант, это поменять местами DOM и CSS, то есть не для каждого элемента DOM искать селекторы CSS, а для каждого селектора CSS искать DOM элементы, что частично соответсвует человеческой логики, но я не понимаю как это может эффективно работать с точки зрения машины.
                        +1
                        O! Про «как» отличная статья вышла еще год назад — hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo
                          +2

                          Да, я глянул статью "Внутри супер-быстрого CSS-движка: Quantum CSS (aka Stylo)" и даже исходный код посмотрел, так как сам когда-то свой велосипед писал и мне интересно как эту логику реализуют другие. Оптимизации и распараллеливание заслуживают отдельного внимания, отсюда и может идти разговор о стоимости, особенно если стоимость определять в контексте усредненного приложения, тут даже есть предмет для исследований. Но общая логика алгоритма такая, что проверка селектора происходит рекурсивно, от потомка к родителю, а следовательно совет в статье логичный и является всего лишь следствием из алгоритма, никаких исследований для этого следствия не нужно.


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

                        0

                        Сейчас даже часы могут ракету на Луну отправить, а всё то же, медленные цсс селекторы.

                    +2
                    Не ожидал прочитать в 2019 баяны из 2010.
                      +1
                      И правда, об этих вещах не пишут в starter tutorial. И многие о них так и не узнают, так как на большинстве проектов производительность css находится где-то в пределах погрешности. Сам я этой темой заинтересовался, когда начал пилить enterprise с чрезвычайно перегруженным dom. Теперь на собеседованиях фронтов и верстальщиков часто задаю вопросы о производительности css, как минимум на уровне приоритета селекторов и примеров «дорогих» css свойств. Как показывает практика — знание не очень распространенное
                        +1
                        Сам я этой темой заинтересовался, когда начал пилить enterprise с чрезвычайно перегруженным dom

                        на большинстве проектов производительность css находится где-то в пределах погрешности.

                        Теперь на собеседованиях фронтов и верстальщиков часто задаю вопросы о производительности css

                        Это что, такая форма доминирования? Унижения? Издевательства? Вещь редкая, нужна мало где, так давайте как я буду её спрашивать у верстальщиков (которые зачастую новички) либо фронтов (которые мало касаются верстки)

                          0
                          Абсолютно нет, никакого доминирования и унижения. Эти вопросы не являются определяющими, основа — базовые знания JS и несколько задач опять таки по базовым знаниям.
                          Вещь редкая, нужна мало где

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

                          Может быть я не прав, но если фронт не умеет толково верстать — это у меня вызывает непонимание
                            0
                            Уже некоторое время как существует мир веб-приложений. Фронт может быть хорошим инженером и отлично уметь архитектуру\развязывание логики. Если ему в пару дать верстальщика, то все будут заняты своим делом и процесс будет очень хорошо распараллелен. Естественно, такой фронт может что-то сверстать, но это не будет «толково», потому что чистый верстальщик оттачивает этот навык каждый день.
                              0
                              Могу оказаться неправ, но редко кто желает быть верстальщиком на постоянной основе. Поэтому часто позиция верстальщика совмещается с джун позицией на фронте, со временем человек переходит в разработку, но память о боевой юности никуда не пропадает, поэтому такой фронт умеет верстать.
                              Если рассмотреть вариант когда человек сразу начинает как фронтенд софтвеер энжинир, не знает ничего про верстку, но пытается натянуть бизнесовую логику на непонятные особо конструкции языка разметки, манипулируя со стилями и DOM, особо не понимая как это устроено — я опять оказываюсь удивлен.
                                –1
                                но пытается натянуть бизнесовую логику на непонятные особо конструкции языка разметки

                                А он и не должен это (натягивать логику на нтмл) делать — для примера на Реакте он может обеспечить разбивку на компоненты, и передавать этим компонентам правильные props-ы, а верстальщик уже на JSX верстает "тупые" компоненты

                          0
                          чрезвычайно перегруженным dom

                          Может сперва эту проблему решите, а потом уже будете "оптимизировать селекторы"?

                          +1
                          Шаг 0: некоторым не говорили, что это «си эс эс», а не «ка эс эс»
                            +2
                            Мы сейчас говорим о «цэ эс эс»?
                            0
                            Как я люблю описание на манер «Так надо делать, и не задавайте лишних вопросов»

                            Некоторые из дорогих свойств:
                            border-radius
                            box-shadow
                            filter
                            :nth-child
                            position: fixed

                            Почему свойства border-radius / box-shadow / filter являються ресурсо затратными это понятно, но вот почему к этому списку припадает :nth-child? Не думаю что он настолько же тяжелый как тот же box-shadow, а так же насколько сильно отличаеться :nth-child(2n) от :nth-child(2) по производительности.
                            Так же можно было немного сказать про position: fixed / absolute, что они отрисовываються вообще отдельно, и так же просчитываються отдельно.
                              0

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

                                0
                                Так вот и я так же думаю, все зависит от вложений, и как ими пользоваться, сам же псевдокласс в себе ничего затратного не имеет, как и остальные псевдоклассы, так что не понимаю почему его сравнивают с намного процессоемкими CSS свойствами. Да и как по мне тема затрат на вычисления довольно таки обширная, и тут нельзя просто сказать что данные свойства лучше избегать, а другие можно использовать повсеместно.
                              0
                              О, вопрос по теме:
                              Допустимы ли inline комментарии наподобие:
                              something: /*comment*/red;
                              
                                0
                                CSS валиден, но не красив, я бы не стал так писать.
                                  0
                                  Мне это нужно для poor man's препроцессинга как метки определенных точек.
                                  Просто IE отказался читать такое, хотя вроде по стандарту комментарий может быть где угодно между term'ами. Вот и думаю — то ли IE сачкует, то ли я стандарт неправильно понимаю.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое