Edge ненавидит ваши атрибуты

в 12:00, , рубрики: AngularJS, attributes, css, edge, internet explorer, performance, render time, атрибуты, браузеры, время отрисовки, производительность, Разработка веб-сайтов

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

Сказ о том, что все браузеры — атрибутофобы, а некоторые особенно.

Edge ненавидит ваши атрибуты - 1

Предыстория

Однажды один из моих коллег обратился ко мне с предложением подумать вместе над одной весьма занимательной задачей: демо C3 (библиотека для отображения графиков) при копировании в наше AngularJS приложение становилось настолько медленным при использовании Edge, что начинало целиком и полностью соответствовать поговорке "тише едешь — дальше будешь". Дальше от заказчика, текущего и всех будущих потенциальных проектов.
Первая мысль — проверим в других браузерах. Chrome и Firefox упорно настаивала на том, что у нас все хорошо.

Значит дело в зависимостях. Начали убирать зависимости из angular.module(). После того, как там не осталось ровным счетом ничего наше положение было все таким же бедственным.
Следующим шагом было использование встроенного в dev tools performance tracker'а. К сожалению, мы долгое время смотрели совсем не туда, анализируя снова и снова время вызова различных JS функций, пока, совершенно отчаявшись, мы не взглянули на время обработки CSS. Стали разбираться. Сразу удивило то, что после чистки проекта размер собранного и минифицированного CSS файла все еще был внушительным. Оказалось, что в этом проекте была подключена библиотека flex-attr, которой никто не пользовался и которая просто приехала по наследству от бойлерплейта.

Что же такое могла делать эта библиотека, что она почти что вешала Edge на пару секунд?
Flex-attr — это набор CSS правил для удобной работы с flex'ми прямо из HTML. В первые я столкнулся с этим подходом, когда использовал Angular Material для своего проекта. В последствии с Angular Material было решено распрощаться, но подход мне понравился, и я сделал свой форк, оставив только CSS и SCSS, портировав его под LESS и добавив столь необходимую мне возможность переопределять и добавлять новые постфиксы и размеры экранов (breakpoints).

На свой проект коллега получил эту библиотеку, использовав наш внутренний бойлерплейт, в котоорый я ее самолично добавил немногим ранее. Что это за жизнь, если ты регулярно не стреляешь себе в ноги, не правда ли? Главное — не в голову и не из дробовика как Кобейн, но у меня еще есть время до 27, так что можно не волноваться.

Как следует из описания, эта библиотека — это CSS файл с большим количеством правил для атрибутов ( [your-attribute] ). После ее отключения проект ожил и забегал как пасхальный кролик. Однако осадок остался. Захотелось бенчмарка, чтобы понять глубину проблемы (может, конечно, и потому, что у моя жизнь пуста и только работа может как-то развлечь, но мы же культурные люди и не будем предполагать таких пошлых вещей?). Захотелось ответов. Захотелось понимания того, как необходимо эту библиотеку переписать, чтобы она перестала быть одним из пыточных приборов святой инквизиции.

Бенчмарк

Для измерения производительности был написан простой скрипт на Python. Его задача создать набор html файлов с различным количеством блоков и CSS правил для классов и атрибутов, а также втсавить туда JS код для определения времени исполнения.

Структура HTML файла:

<!doctype html>
<html>
<head>
<title>CSS Tag Selctor Test</title>
</head>
<body>
    <style>
        CSS goes here
    </style>
    <script type="text/javascript">var renderTime = Date.now();window.addEventListener("load", () => console.log(Date.now() - renderTime))</script>

    <div>0</div>
    <div>1</div>
    <div>2</div>
    ...
    <div>n</div>
</body>
</html>

Каждый div имеет либо класс,

<div class="test-selector-0">0</div>

либо атрибут

<div test-selector-0>0</div>

Каждое CSS правило задает background-color для блока. Каждому блоку соответствует лишь одно правило. Если количество правил меньше количества блоков, то они применяются снова с начала. Время загрузки (в мс) определяется по событию load и выводится в консоль.

Для тестов была создана новая виртуальная машина с Windows 10 Enterprise с 4Гб RAM и 2 ядрами (если быть точнее, то это одно физическое ядро, но два потока выполнения, благодаря Hyper Threading). Были использованы браузеры Microsoft Edge 38.14393.0.0, Google Chrome 60.0.3112.101, Mozilla Firefox 55.0.2. В каждый момент времени был открыт только один браузер, в которым была открыта только одна вкладка. Во избежания влияния каких-либо системных процессов на результаты бенчмарка каждый тест был запущен по 10 раз для каждого браузера и было взято медианное значение. Исключение составили тесты "10000 блоков/10000 правил для атрибутов", "50000 блоков/10000 правил для атрибутов" для Edge. Было произведено лишь 5 и 3 замера соответственно, т.к. мне было попросту лень ждать рндера страницы по 5-6 минут. В дальнейшем я буду оперировать лишь медианными значениями. С полными результатами вы можете ознакомиться здесь.

Результаты для 100 CSS правил

Blocks Chrome
(calsses)
Firefox
(classes)
Edge
(classes)
Chrome
(attributes)
Firefox
(attributes)
Edge
(attributes)
100 4 16.5 30 4 18 23.5
1000 16.5 95.5 44 19 125.5 139.5
10000 437.5 653 382 452 585 1338.5
50000 3489 2565 5176 3566.5 2865.5 7061

Edge ненавидит ваши атрибуты - 2

Edge ненавидит ваши атрибуты - 3

Результаты для 1000 CSS правил

Blocks Chrome
(calsses)
Firefox
(classes)
Edge
(classes)
Chrome
(attributes)
Firefox
(attributes)
Edge
(attributes)
100 12.5 20.5 16.5 17.5 38.5 128
1000 40.5 115.5 127.5 96.5 198.5 647.5
10000 479 618.5 974 960 1242 4578
50000 3719.5 2877.5 5358.5 5936 6582.5 26597.5

Edge ненавидит ваши атрибуты - 4

Edge ненавидит ваши атрибуты - 5

Результаты для 10000 CSS правил

Blocks Chrome
(calsses)
Firefox
(classes)
Edge
(classes)
Chrome
(attributes)
Firefox
(attributes)
Edge
(attributes)
100 99 33 24 156.5 134.5 1034.5
1000 149 85 77.5 618 8760 5185.5
10000 655 625.5 788 5466 11020.5 46623
50000 3681.5 3183.5 4743.5 36779 50663.5 326838

Edge ненавидит ваши атрибуты - 6

Edge ненавидит ваши атрибуты - 7

Учитывая тот факт, что библиотека, послужившая первопричиной данного исследования, была подключена к исходному приложению, но ни одного класса из нее не было использовано, было решено провести еще один тест: сравнить производительность для тех случаев, когда CSS правила действительно применяются в HTML и когда нет (т.е. в CSS они будут, но никто их использовать не будет). Для этого был добавлен параметр --no_apply_css в первоначальный бенчмарк.

Результаты для 10000 CSS правил (если в скобках ничего не указано, то CSS приавла были использованы в HTML)

Blocks Chrome Firefox Edge Chrome
(No CSS applied)
Firefox
(No CSS applied)
Edge
(No CSS applied)
100 156.5 134.5 1034.5 177 84.5 1213.5
1000 618 8760 5185.5 689 562 6037
10000 5466 11020.5 46623 6700 5156 76401
50000 36779 50663.5 326838 38750 34351 431700

Edge ненавидит ваши атрибуты - 8

Edge ненавидит ваши атрибуты - 9

Вывод

Как можно было заметить, производительность всех браузеров деградирует при использовании атрибутов, начиная с определенного размера CSS файла. Все же Edge и тут отличился и не посрамил своей репутации самого быстрого браузера на нашем голубом шарике под названием Земля. Что с этим делать? Не использовать атрибуты для написания вашего CSS. Также стоит очень внимательно смотреть на те библиотеки, которые вы подключаете: они могут привезти с собой пачку столь нежеланных правил для атрибутов. И как показал последний эксперимент, даже если вы не используете ни одного правила для атритутов на вашей страниц, где вы решили вывести огромный список или таблицу (вопрос "зачем?" оставим за рамками этого обсуждения), производительность все равно пострадает. Если по какой-то причине использования большого количества атрибут-селекторов не избежать, то стоит подумать о том, чтобы собирать их в отдельный CSS файл, и загружать этот файл только в случае крайней необходимости. Для себя я решил попросту перевезти flex-attr на классы и порефакторить существующие проекты, когда работа будет завершена.

Если вам интересно посмотреть на экспериментальные данные в каком-то другом разрезе, то скопирутей гуглодок и агрегируйте данные по своему усмотрению.

Автор: keenondrums

Источник


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js