В сегодняшнем типичном сценарии, когда средний веб-сайт отправляет 500 КБ сжатого JavaScript и 1,5 МБ изображений, работающих на среднем устройстве Android через 3G с временем прохождения сигнала 400 мс, производительность CSS-селектора является наименьшей из наших проблем.
Тем не менее, есть что сказать по этой теме, особенно чтобы отсеять некоторые мифы и легенды, которые их окружают. Итак, давайте погрузимся прямо в.
Основы CSS-парсинга
Во-первых, чтобы попасть на ту же страницу — эта статья не о производительности свойств и значений CSS. Здесь мы рассмотрим стоимость производительности самих селекторов. Я сосредоточусь на движке рендеринга Blink, в частности Chrome 62.
Селекторы можно разбить на несколько групп и (примерно) отсортировать от наименее до самых дорогих:
ранг | тип | пример |
---|---|---|
1. | Я БЫ | #classID |
2. | Учебный класс | .class |
3. | Тег | div |
4. | Генеральный и соседний брат | div ~ a , div + a |
5. | Ребенок и потомок | div > a , div a |
6. | универсальный | * |
7. | атрибут | [type="text"] |
8. | Псевдоклассы и элементы | a:first-of-type , a:hover |
Значит ли это, что вы должны использовать только идентификаторы и классы? Ну не совсем. По-разному. Сначала рассмотрим, как браузеры интерпретируют селекторы CSS.
Браузеры читают CSS справа налево. Самый правый селектор в составном селекторе известен как ключевой селектор. Так, например, в #id .class > ul a
, селектор ключа — a
. Браузер сначала сопоставляет все ключевые переключатели. В этом случае он находит все элементы на странице, которые соответствуют селектору. Затем он находит все элементы ul
на странице и фильтрует элементы a
только по тем элементам, которые являются потомками ul
s, и так далее, пока не достигнет крайнего левого селектора.
Поэтому чем короче селектор, тем лучше. Если возможно, убедитесь, что ключевым селектором является класс или идентификатор, чтобы он был быстрым и конкретным.
Измерение производительности
Бен Фрейн создал серию тестов для измерения производительности селекторов еще в 2014 году. Тест состоял из огромного DOM, состоящего из 1000 идентичных элементов, и измерения скорости, необходимой для анализа различных селекторов, начиная с идентификаторов и заканчивая некоторыми сложными и длинными сложными селекторами. Он обнаружил, что разница между самым медленным и самым быстрым селектором составляла ~ 15 мс.
Однако это было еще в 2014 году. С тех пор многое изменилось, и запоминание правил практически бесполезно в постоянно меняющейся среде браузера. Всегда не забывайте делать свои собственные тесты, особенно когда речь идет о производительности.
Я пошел, чтобы сделать свои собственные тесты, и для этого я использовал тест Пола Льюиса, упомянутый в комментарии Пола Айриша, выражая озабоченность по поводу полезных, но извилистых « селекторов количества »:
Эти селекторы являются одними из самых медленных. На 500 медленнее, чем что-то дикое, например, «div.box:not(:empty):last-of-type .title». Тестовая страница http://jsbin.com/gozula/1/quiet
Тест был увеличен до 50000 элементов, и вы можете проверить его самостоятельно . Я выполнил в среднем 10 прогонов на своем MacBook Pro 2014 года, и получил следующее:
селектор | Время запроса (мс) |
---|---|
div |
4,8740 |
.box |
3,625 |
.box > .title |
4,4587 |
.box .title |
4,5161 |
.box ~ .box |
4,7082 |
.box + .box |
4,6611 |
.box:last-of-type |
3,944 |
.box:nth-of-type(2n - 1) |
16,8491 |
.box:not(:last-of-type) |
5,8947 |
.box:not(:empty):last-of-type .title |
8,0202 |
.box:nth-last-child(n+6) ~ div |
20,8710 |
Результаты, конечно, будут различаться в зависимости от того, используете ли вы querySelector
или querySelectorAll
и количество подходящих узлов на странице, но querySelectorAll
приближается к реальному варианту использования CSS, который нацелен на все соответствующие элементы.
Даже в таком крайнем случае, когда нужно сопоставить 50000 элементов и использовать некоторые действительно безумные селекторы, такие как последний, мы обнаруживаем, что самый медленный — ~ 20 мс, а самый быстрый — простой класс с ~ 3,5 мс. Не очень большая разница. В реалистичном, более «прирученном» DOM с около 1000–5000 узлов вы можете ожидать, что эти результаты упадут в 10 раз, что приведет к скорости разбора менее миллисекунды.
Из этого теста видно, что на самом деле не стоит беспокоиться о производительности селектора CSS. Только не переусердствуйте с псевдо-селекторами и действительно длинными селекторами. Мы также можем видеть, как Blink улучшился за последние два года. Вместо заявленного ~ 500-кратного замедления для «селектора количества» ( .box:nth-last-child(n+6) ~ div
) по сравнению с «селектором безумия» ( .box:not(:empty):last-of-type .title
), мы видим только ~ 1.5x замедление. Это удивительное улучшение, и мы можем ожидать только улучшения браузеров, что делает производительность CSS-селекторов еще менее эффективной.
Тем не менее, вы должны придерживаться использования классов, когда это возможно, и принять какое-то соглашение о пространстве имен, такое как BEM, SMACSS или OOCSS, поскольку это не только повысит производительность вашего веб-сайта, но и значительно облегчит поддержку кода. Сверхквалифицированные составные селекторы, особенно при использовании с тегами и универсальными селекторами, такими как .header nav ul > li a > .inner
являются чрезвычайно хрупкими и источником многих непредвиденных ошибок. Их также нужно поддерживать, особенно если вы наследуете код от кого-то другого.
Качество важнее количества
Большая проблема просто иметь дорогие селекторы — их много . Это известно как «раздувание стиля», и вы, вероятно, часто видели проблему. Типичными примерами являются сайты, которые импортируют целые CSS-фреймворки, такие как Bootstrap или Foundation, используя при этом менее 10% переданного CSS. Другой пример можно увидеть в старых, никогда не подвергавшихся рефакторингу проектах, CSS которых перешёл в, как я их называю, «Хронологические таблицы стилей» — CSS с кучей добавленных классов в конце файла, когда проект изменился и вырос теперь больше похож на заросший сад, полный сорняков.
Для передачи большого файла CSS требуется не только больше времени (а сеть является самым узким местом в производительности веб-сайта), но и для анализа. Помимо создания DOM из вашего HTML, браузер должен создать CSSOM (CSS Object Model), чтобы сравнить его с DOM и сопоставить селекторы.
Таким образом, сохраняйте свой стиль сухим и сухим, не включайте в себя все и кухонную раковину, загружайте то, что вам нужно и когда вам это нужно, и используйте UNCSS, если вам нужно.
Если вы хотите больше узнать о том, как браузеры анализируют CSS, посмотрите статью Николь Салливан о Webkit , статью Ильи Григорика о том, как это делает Blink , или статью Лин Кларк о новом движке Mozilla Stylo CSS .
Слон в комнате: недопустимость стиля
То, что мы уже рассмотрели, хорошо, но мы обсудили только один проход рендеринга. Современные веб-сайты больше не являются статичными документами, а напоминают приложения с динамическим контентом, с которым пользователи могут взаимодействовать.
Это усложняет ситуацию, поскольку синтаксический анализ CSS — это только один шаг в конвейере рендеринга браузера. Вот ориентированное на рендеринг представление о том, как браузер отображает один кадр на экране (источник: Google ):
Мы не будем вдаваться в производительность и компоновку JavaScript, а сосредоточимся вместо этого на фиолетовом разборе стиля элементов и разметке элементов.
После создания DOM и CSSOM браузер должен объединить их в дерево рендера, прежде чем, наконец, нарисовать его на экране. На этом этапе браузер должен выяснить вычисленный CSS для каждого соответствующего элемента. Вы можете сами убедиться в этом на панели « Элементы»> «Стили» инструментов разработчика. Для создания окончательного, вычисленного CSS для элемента требуются все соответствующие стили, каскад и стили пользовательского агента, специфичные для браузера.
Затем он может перейти к шагу макета (также известного как перекомпоновка), где он вычисляет геометрию и создает блочную модель страницы, размещая каждый элемент на соответствующей позиции в области просмотра. Макет является наиболее интенсивной в вычислительном отношении частью этого процесса.
Наконец, браузер преобразует каждый узел в дереве рендеринга в фактические пиксели на экране на этапе рисования.
Теперь, что происходит, когда мы изменяем DOM, изменяя некоторые классы на странице, добавляя или удаляя некоторые узлы, изменяя некоторые атрибуты или каким-либо образом связываясь с HTML ( или самими таблицами стилей )?
Мы лишаем законной силы вычисленные стили, и браузер должен лишить законной силы все вниз по дереву соответствующих селекторов. Хотя современные браузеры намного умнее, раньше вы меняли класс элемента body
, и все элементы-потомки должны были пересчитать свои вычисленные стили.
Один из способов избежать этой проблемы — уменьшить сложность ваших селекторов. Вместо того, чтобы писать #nav > .list > li > a
, используйте один селектор, например .nav-link
. Таким образом, вы уменьшаете объем аннулирования стиля, поскольку, если вы измените что-либо внутри #nav
, вы не будете запускать пересчеты для всего узла.
Другой способ — уменьшить область действия, например количество недействительных элементов. Будьте конкретны с вашим CSS. Помните об этом, особенно во время анимации, когда браузер имеет всего ~ 10 мс для выполнения всей необходимой работы.
Если вы хотите перейти к мельчайшим деталям аннулирования стиля, я рекомендую прочитать Invalidation в Blink .
Вывод
Подводя итог, вы не должны беспокоиться о производительности селектора, если вы действительно не за борт. Хотя эта тема была в моде в 2012 году, с тех пор браузеры стали намного быстрее и умнее. Даже Google больше не беспокоится об этом. Если вы заглянете на страницу Google Page Speed Insights , вы не увидите правило «Использовать эффективные селекторы CSS», которое было удалено в 2013 году.
Вместо этого сосредоточьтесь на том, чтобы сделать ваш CSS понятным и понятным. Ваши коллеги и ваше будущее я буду благодарен вам за это. Попробуйте оптимизировать доставку CSS, включив только используемые стили. И после этого познакомьтесь с конвейером рендеринга. В отличие от селекторов, сами стили могут быть дорогими, и различие между нестандартным и гладким сайтом часто можно найти в том, как реализован CSS.
И в заключение: всегда делайте свои собственные тесты.
Не просто верьте тому, что кто-то написал в Интернете несколько лет назад. Пейзаж резко меняется и невероятными темпами. То, что актуально сегодня, может устареть раньше, чем вы знаете.