Оптимізація CSS: ID-селектори та інші міфи

10 хв. читання

Типовий веб-сайт сьогодні нараховує близько 500 КБ стиснутого JavaScript та 1.5 MБ зображень, що завантажуються за 400 мілісекунд на середньому Android-пристрої за допомогою 3G. Зважаючи на це, тривалість обробки CSS — найменша з незручностей.

Тим не менш, розглянемо питання оптимізації CSS докладніше аби вилучити усі міфи та упередження.

Основи обробки CSS

У статті ми не будемо детально розглядати особливості функціонування СSS-значень та властивостей, натомість зупинимося на продуктивності самих селекторів. У статті посилатимусь на рушій Blink, зокрема на Chrome 62.

Cелектори можна умовно розбити на декілька груп та приблизно відсортувати від найменш до найбільш продуктивного.

Місце Тип Приклад
1. ID #classID
2. Клас .class
3. Tег div
4. Узагальнений та сусідський селектори div ~ a, div + a
5. Дочірній селектор та нащадок div > a, div a
6. Універсальні стилі *
7. Атрибути [type="text"]
8. Псевдокласи та елементи a:first-of-type, a:hover

Чи означає це, що варто використовувати лише ID та класи? Не зовсім. Спершу, розглянемо як браузери інтерпретують CSS-селектори.

Зчитування CSS-селектора браузером відбувається справа наліво. У складених селекторах той, що знаходиться правіше, називається ще ключовим селектором. Наприклад, у селекторі #id .class > ul a ключовим селектором є а. Браузер зіставляє спочатку всі ключові селектори. У такий спосіб, він знаходить на сторінці всі елементи, що відповідають селектору а, потім знаходить всі ul елементи та фільтрує елементи а— нащадки ul. Цей процес продовжується доки не досягнуто крайнього лівого селектора.

Тому чим коротше селектор, тим краще. Намагайтеся організовувати ключові селектори як класи або ID, аби досягти максимальної швидкості та конкретності.

Замір продуктивності

У серії тестів, створеній ще у 2014 році, виконується замір ефективності селекторів. Тест полягав у вимірюванні швидкості на обробку близько 1000 селекторів у DOM, починаючи від ідентифікаторів до досить серйозних та довгих складених селекторів. З'ясувалось, що різниця між найшвидшим і найповільнішим складала близько 15мс.

Багато чого змінилося з моменту проведення тесту, тому немає сенсу запам'ятовувати певні правила. Важливо, натомість, проводити власні випробування, особливо коли ви зацікавлені у продуктивності.

У власному випробуванні я використав тест Пола Левіса, який занепокоєний з приводу «кількісних селекторів»:

Такі селектори найповільніші з-поміж усіх можливих. Приблизно у 500 раз повільніші, ніж щось дивне, на зразок div.box:not(:empty):last-of-type .title.

У тесті було залучено більш ніж 50 000 елементів. Було отримано такі результати (можна перевірити власноруч):

Селектор Тривалість запиту (мс)
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.

Навіть у тестовому варіанті, де відбувається пошук та зіставлення приблизно 50 0000 елементів, а використовуються селектори, на зразок останнього у таблиці, було виявлено, що найповільніша тривалість запиту ~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.5 рази. Тож, можемо лише сподіватися, що браузери будуть ще більше вдосконалюватися, зменшуючи вплив продуктивності CSS-селекторів.

Тим не менш, слід застосовувати класи, де це можливо, а також дотримуватись певних правил іменування, на зразок BEM, SMACSS або OOCSS, тому що це не лише підвищить продуктивність сайту, але й зробить ваш код зручним для налагодження. Складені селектори, особливо ті, що застосовуються із тегом, та універсальні селектори на зразок .header nav ul > li a > .inner спричиняють багато помилок. Основна складність полягає у налагодженні такого коду, особливо після попереднього розробника.

Кількість не означає якість?

Великою проблемою селекторів є їх надлишок або «роздування стилів», що зустрічається досить часто. Типовими прикладами слугують сайти, які імпортують увесь CSS з фреймворків на зразок Bootstrap або Foundation, у той же час як використовують не більше 10% з усіх стилів. Інший приклад — старі проекти, що не зазнавали рефакторингу. Їх CSS перетворюється на радше «Хронологічні таблиці стилів» з великою кількістю класів, що додавалися у кінець файлу, коли проект розростався. Звичайно, такий підхід не додає зручності.

Зверніть увагу, що передача та обробка великих CSS-файлів займає багато часу (а передача даних є найбільш вузьким місцем у продуктивності сайту). Окрім формування DOM з вашого HTML, браузеру необхідно створити CSSOM (CSS Object Model) для порівняння із DOM та зіставлення селекторів.

Тож, застосовуйте більш «чисті» CSS-селектори, завантажуйте лише те, що вам потрібно та, за необхідністю, використовуйте UNCSS.

Для більш глибокої інформації про обробку CSS браузерами ознайомтеся: Webkit, Blink, новий CSS рушій Stylo .

Недійсність стилів

Сьогодні веб-сайти більше не є статичними документами, а скоріше схожі на застосунки з динамічним контентом, з яким взаємодіють користувачі.

Обробка CSS — лише один із багатьох етапів, які необхідно пройти для кінцевого завантаження сторінки у браузері. Поглянемо як браузер здійснює обробку з точки зору показу сторінки (джерело: Google).

rendering

Не будемо вдаватися у подробиці продуктивності JavaScript та композиції, натомість зупинимось детальніше на обробці та розташуванні елементів на сторінці.

Після конструювання DOM та CSSOM браузеру необхідно об'єднати їх у дерево візуалізації, а вже потім показати на екрані. На цьому кроці браузеру треба визначити розрахований CSS для кожного відповідного елемента. Можна переконатися у цьому самостійно на панелі Elements > Styles інструментів розробника. Для створення остаточного розрахованого CSS для елемента враховуються всі відповідні стилі, каскадування, а також специфічні користувацькі налаштування у браузері.

Наступним кроком є компонування (також відомий як reflow), коли виконуються розрахунки, створюється модель сторінки, де кожен елемент займає відповідне положення на екрані. Найбільш трудомістким є процес розрахування макету.

Нарешті, браузер перетворює кожен вузол у дереві візуалізації на фактичні пікселі на екрані під час відтворення.

Що ж відбувається, коли ми видозмінюємо DOM шляхом заміни деяких класів на сторінці, додаємо або видаляємо вузли, атрибути, або яким-небудь чином чіпаємо HTML (чи безпосередньо таблиці стилів)? Таким чином ми анулюємо розраховані стилі, тому браузеру необхідно анулювати усе дерево, що знаходиться нижче відповідних селекторів. Раніше, якщо ви, наприклад, змінювали клас елементу body, усі елементи-нащадки повинні були перерахувати свої стилі.

Одним зі способів уникнення цієї проблеми є скорочення назви селекторів. Замість використання #nav > .list > li > a спробуйте щось на зразок .nav-link. Таким чином, область недійсності стилю зменшиться: якщо ви зміните що-небудь у #nav , то перерахування всього вузла не відбудеться.

Інший спосіб вирішення проблеми - зменшення кількості недійсних елементів. Майте це на увазі особливо при застосуванні анімацій, коли у браузера є лише 10 мс щоб усе обробити.

Для поглиблення знань: тут можна дізнатися більше про недійсність стилів на прикладі Blink.

Висновок

Тож, не варто турбуватися про продуктивність селекторів, якщо ви застосовували їх з розумом. До того ж, браузери стали швидшими та практичнішими.

Цікаво, що навіть Google видалив правило «Використання ефективних CSS-селекторів» з «Порад щодо швидкості сторінок».

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

І наостанок: завжди проводьте власні тестування. Не довіряйте інформації, яка була в мережі декілька років тому. Тенденції змінюються кардинально і з неймовірною швидкістю. Те, що актуально сьогодні, застаріє раніше, ніж ви думаєте.

css
Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 4.7K
Приєднався: 10 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація