Ще один спосіб стиснення CSS файлів

image

На зображенні вище багато побачать відому картину. Так виглядає більшість CSS файлів на продакшені. Ми всі прагнемо, щоб наші веб-сторінки завантажувалися швидко; для досягнення цієї мети використовуємо різні інструменти і техніки оптимізації завантаження і рендеринга сторінок. Про одному, але рідко використовуваному методі, я б хотів поговорити і розповісти, як мені вдалося скоротити розмір CSS файлу пошти mail.uk на 180Кб.

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

Далі ми спробуємо вичавити всі соки з цього файлу.

Якщо придивитися, то ми помітимо в цьому файлі дещо цікаве.

image

Прошу звернути увагу на імена класів. Напевно, ви вже здогадалися, про що піде мова — про скорочення довжини цих імен. Тому що з приходом БЕМ та інших технік написання CSS назви класів стали досить довгими, а часом непристойно довгими.

Приклади з життя

Я провів аналіз CSS файлів, деяких популярних ресурсів і підтвердив своє припущення:

image
На зображення вказані класи, що зустрічаються в продакшан версіях CSS файлів зазначених сайтів

Напевно, нікому не треба пояснювати значимість початкової швидкості завантаження сторінки і її вплив на задоволеність користувачів, конверсію, відвідуваність сайтів. З приходом мобільних девайсів і мобільного інтернету ця проблема стоїть дуже гостро. Наприклад, завантаження 40Кб на 4G інтернеті в середньому займе 700мс, а якщо розглянути 3G, EDGE, GPRS, WiFi в годину пік в кафе або метро, то час завантаження значно збільшиться. Кожні зекономлені кілобайти на вагу золота.

Якщо ми боремося за кожен пробіл і перенесення рядка в CSS файли, нещадно вирізаючи їх, то чому б не стискати назви класів і ідентифікаторів до двох чи трьох символів? Адже вашим користувачам абсолютно не важливо, як буде називатися клас блоку на сторінці, але їм буде дуже важлива швидкість завантаження сторінки. Можливо, саме тому хлопці з Google використовують цю техніку (на сайті google.com):

image

Від слів до справи

Отже, з тим, навіщо це потрібно, сподіваюся, розібралися. Приступимо до справи. Перша проблема в тому, що ми не можемо просто так взяти і скоротити класи і ідентифікатори в CSS файли, тому що вони використовуються у верстці, шаблони та JS файли. Замінивши тільки в одному місці — все зламається. Стає очевидно, що треба замінити назви у всій зв'язці файлів. У своїх проектах для минификации, склеювання та інших таких завдань я використовую Grunt.js. Ця система складання є однією з найпопулярніших світі фронтенд-розробки. Займаючись швидким пошуком плагінів, що реалізують таке завдання, я нічого не знайшов і вирішив написати свій велосипед плагін.

grunt-revizor

Так народився grunt-revizor. При його написанні я зіткнувся з кількома проблемами. В CSS файлах все просто, синтаксис заздалегідь відомий .name або #name, але в інших файлах це може виглядати інакше. Наприклад, в HTML <div class="name"></div>, тобто name вже без крапки або в JS файли document.getElementById('name') теж без #, що ускладнює проблему пошуку і заміни імен. Що найбільш страшно — можна зламати що-небудь, наприклад, якщо ім'я CSS класу буде ".success" і в JS файл у нас буде мінлива «success», замінивши її ми поламаємо JS код. А цього ми допустити не можемо, тому довелося ввести вимоги до написання імен, а саме — ім'я повинне закінчуватися на унікальний префікс, який однозначно дасть можливість відрізнити клас від чогось іншого JS, HTML і інших файлах. Наприклад, .b-tabs__title-block--, в даному випадку префікс '--'.

Приклад тягаючи:

grunt.initConfig({
revizor: {
options: {
namePrefix: '__',
compressFilePrefix: 'min'
},
// Знайде файли: test/css/style1.css, test/css/style2.css 'test/js/main.js та ін...
src: ['test/css/*.css', 'test/html/*.html', 'test/js/*.js'], 
dest: 'build/'
},
});

Після запуску цього тягаючи grunt знайде всі CSS, HTML та PHP файли, які відповідають шляхах з src, знайде в CSS файлів усі імена класів та ідентифікаторів, які закінчуються на префікс __, стисне знайдені імена до двох і трьох символьних, наприклад .b-tabs--notselected__ -> .zS, після чого збереже нові файли, в яких будуть замінені всі знайдені збіги, і збереже їх у папці build, з іменами style1-min.css, index-min.html, main-min.js.

Приклад CSS:

/* Original: style1.css */
.b-tabs__title-block-- {
color: red;
font-size: 16px;
}
.b-tabs__selected-- {
font-weight: bold;
}
/* ======================================= */
/* Result: style1-min.css */
.eD {
color: red;
font-size: 16px;
}
.rt {
font-weight: bold;
}

Приклад JS:

/* Original: main.js */
var $tabmenu = $('#tabmenu--');
var tabmenu = document.getElementById('tabmenu--');
if ($tabmenu.hasClass('b-tabs__selected--')) {
$tabmenu.removeClass('b-tabs__title-block--');
};

/* ======================================= */
/* Result: main-min.js */
var $tabmenu = $('#j3');
var tabmenu = document.getElementById('j3');
if ($tabmenu.hasClass('rt')) {
$tabmenu.removeClass('eD');
};

Такий же принцип і для HTML, template і інших файлів.

Деякі особливості плагіна:
  • Спочатку генеруються 2-х символьні імена. Після того, як кількість імен перевалить за 2500, почнуться генеруватися 3-х символьні імена;
  • Для генерації імен використовуються малі та великі букви латинського алфавіту, цифри, символи - та _;
  • Є перевірка на колізію, тобто стислий ім'я може відповідати тільки одного повного імені.


Оптимізації на прикладах

Спробуємо з'ясувати потенційну вигоду такої оптимізації на прикладі тих самих популярних ресурсів. Розповім, як я буду вважати. Звичайно ж, не буду пропускати CSS файли через grunt-revizor, тому що в них не використовується унікальний префікс. З допомогою нехитрого Node файлу я буду вважати загальна кількість класів, вираховувати їх довжину в байтах і з цього вираховувати потенційний розмір файлів. Спрощена формула виглядає так:

names = ['b-block1','b-block2','b-block3',....];
saveSize = allNamesSize - (names.length*3)
// allNamesSize - Загальна довжина всіх символів всіх знайдених класів
// 3 - Підсумкова кількість символів в стислому назва


Сайт Розмір оригінального файлу Стисло У відсотках
Mail.ru пошта (Desktop version) 849 Кб 182,5 Кб 26,4%
hh.ru (Desktop version) два файлу 109,8 Кб 30 Кб 27,3%
vk.com (Mobile version) два файлу 184,1 Кб 48,5 Кб 26,4%
Яндекс.Таксі (Mobile version) 127 Кб 13 Кб 10,3%
2gis.ru (Mobile version) 1293 Кб 172 Кб 13,3%
Використовувалися звичайні файли, не gzip. Дані оптимізації приблизні, тому що по мимо CSS файлів стилі іноді були в html сторінці, відповідно, тут не враховувалися. Використовувалася простенька регулярка для парсингу, яка шукала тільки класи, притому не всі можливі, і вона не шукала ідентифікатори. Крім стиснення CSS буде виграш на інші файли, html, шаблонів і т. д., які тут теж не враховані. Зазначу відразу, що деякі файли мало подаються такої оптимізації, наприклад, файли де мало класів або використовуються короткі імена, чи багато зображень, вставлених через data: «URL», що демонструється на файлах 2gis і Яндекс.Таксі.

Висновок

Звичайно ж, я не придумав нічого нового. Цей спосіб знайомий багатьом, але він використовується рідко, оскільки в ньому є як мінуси, так і плюси. Наприклад, він вносить труднощі в розробку, чистоту коду і т. д. За великим рахунком gzip частково вирішує проблему довгих імен і здорово стискає файли, але все ж певний виграш в розмірі і часу декомпресії файлів можна отримати і з використанням gzip. Наприклад, оптимізований стиснутий файл mail.ru на 20Кб менше того ж не оптимізованого стисненого файлу.

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

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

Загалом, є про що поговорити і над чим замислитися.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.