Система складання фронтенда в CleverStyle Framework або чому вам може бути не потрібна кастомний

CleverStyle Framework всіляко допомагає розробнику не тільки на сервері, але і на фронтенде. Я про це згадував у попередніх статтях, але ніколи не вдавався в подробиці того, як саме все влаштовано під капотом.
Дана стаття буде зануренням в подробиці роботи зі статикою для фронтенда, починаючи від того, як визначаються файли потрібні на сторінці і закінчуючи оптимизациями доставки статики зразок HTTP/2 Server Push. Не забудемо і про те, чому з використанням CleverStyle Framework можна обійтися без кастомних системи складання і при бажанні інтегрувати таку систему складання процеси фреймворка.
Ця стаття спеціально випускає з уваги інтеграцію Bower/NPM і RequireJS, це буде тема окремої статті в недалекому майбутньому.
Що підключати
Перше завдання — визначити які саме файли (CSS/JS/HTML) потрібні на конкретній сторінці, і дзесь є дві умовні групи:
  • загальні файли для усіх сторінок сайту
  • файли, які специфічні для даної сторінки (або для декількох сторінок)
Загальні файли
Загальні файли в свою чергу так само можна розділити на три підгрупи.
У першу підгрупу потрапляють файли ядра системи з директорії
assets
. Там знаходиться той базовий мінімум службових файлів, які потрібні самому фреймворку (насправді це можна обійти, але про це далі). Файли даної підгрупи завантажуються в першу чергу.
У другу підгрупу потрапляють файли поточної теми оформлення інтерфейсу
themes/тема/{css|js|html}
, підключаються відразу після файлів ядра.
У третю групу потрапляють файли будь-яких встановлених модулів, для яких не вказано, на якій сторінці (сторінка штука трохи умовна) вони повинні підключатися (про це трохи далі).
Файли для конкретних сторінок
Конкретні сторінки генеруються встановленими модулями.
В типовому сценарії при установці модуля
Module_name
він буде обробляти сторінки, що починаються з
/Module_name
. Для того, щоб вказати, що на сторінках цього модуля повинні використовуватися конкретні файли складається карта таких файлів асоційованих з шляхами у файлі з мета-даними модуля.
Приклад для модуля Blogs:
{
"package" : "Blogs",
"category" : "modules",
...
"assets" : {
"admin/Blogs" : [
"cs-blogs-admin-*"
],
"Blogs" : [
"cs-blogs-*"
]
},
...
}

У модулі Blogs немає CSS і JS файлів для підключення на сторінку, веб-компоненти (які в свою чергу можуть містити CSS/JS, але це вже інша історія, про яку трохи далі).
У прикладі вище ми бачимо, що на всіх сторінках модуля будуть підключені файли, шляхи яких починаються з префікса
cs-blogs-
(зірочка відкидається і підтримується виключно в цілях поліпшення читабельності), а файли з префіксом
cs-blogs-admin-
будуть підключатися тільки на сторінках адміністрування (цей ключ у списку йде першим, так що спочатку буде відфільтровано файли з даними префіксом, а решта потраплять у наступний ключ, не дивлячись на однаковий початок).
На рахунок шляхів потрібно уточнити що вони відраховуються від наступних коренів:
modules/Modules_name/assets/{css|js|html}
, з формату файлу ясно в якій директорії він перебуває, тобто
modules/Modules_name/assets/html
буде проводитися пошук тільки HTML файлів, але не CSS і/або JS.
Прямі залежності
Файли самого модуля це лише частина історії, важливою ланкою в даній системі є залежності між модулями.
Подивимося на модуль Photo gallery для прикладу одну з його залежностей, модуль Uploader:
{
"package" : "Photo_gallery",
"category" : "modules",
...
"assets" : {
"admin/Photo_gallery" : "admin.css",
"Photo_gallery" : "general.*"
},
...
"provide" : "photo_gallery",
"require" : [
"System>=6.25",
"System<7.0",
"Composer",
"Fotorama>=4.4.9",
"file_upload",
"composer_assets"
],
...
}

{
"package" : "Uploader",
"category" : "modules",
...
"assets" : {
"admin/Uploader" : "admin.css",
"Uploader" : "script.js"
},
...
"provide" : "file_upload",
...
}

Тут ми бачимо що модуть
Photo gallery
має пряму залежність від
file_upload
.
file_upload
в свою чергу є не назвою модуля, а його функціональністю (докладніше про це в документації). З точки зору обробки статики це означає, що файли призначені для модуля
Uploader
(для модуля в цілому, не для підсторінок) будуть так само підключені на сторінках модуля
Photo gallery
, при чому перед власними файлами цього модуля (це важливо якщо залежність має деякий синхронний код, що використовуються залежно далі).
Треба зауважити, що не тільки обов'язкові (ключ
require
), але і опціональні (ключ
optional
) залежно враховуються в даному сценарії.
Зворотні залежності
Для фронтенда так само важливою концепцією є зворотні залежності. Це такі залежності, що вказує не цільовий модуль, а сторонній модуль самостійно. Разом з можливістю змінити веб-компоненти альтернативними реалізаціями це досить потужний інструмент. По суті це надає можливість сказати фреймворку, що ось цей модуль надає додаткову функціональність для іншого модуля (наприклад, змінює вигляд блогу без необхідності редагувати сам модуль блогу). Це досить тонкий момент, їм не варто зловживати, але іноді буває корисним.
Вкладені залежності
Залежно можуть мати власні залежності, в тому числі залежно можуть повторюватися. Фреймворк це розуміє і перетворює дерево залежностей в плоску структуру з урахуванням цієї особливості (не намагайтеся створювати циклічні залежності, результат буде невизначеним).
Важливо зауважити, що залежність від версії системи (модуль
System
) не впливає на циклічність залежностей, ефективно можете вважати що вона буде проігноровано під час обробки статики.
Інтеграція в процес збору файлів для підключення
Під час складання файлів для підключення система генерує подію
System/Page/assets_dependencies_and_map
, підписавшись на яку є можливість маніпулювати зібраними системою файлами а так само побудованої структурою залежностей.
В цю структуру можна додавати власні файли, організовувати їх в такі собі віртуальні модулі і добудовувати залежності. Таким чином модуль
Composer assets
интегрирует підключення файлів з Bower/NPM пакетів під фреймворк щоб скористатися його внутрішніми механізмами для обробки файлів (про них трохи далі).
Власне підключення файлів на сторінку
Фреймворк не завжди просто підключає всі потрібні для конкретної сторінки, файли, що надходить більш просунутим чином в залежності від конфігурації системи.
На те, як підключати файли впливають наступні параметри:
  • кешування і стиснення
  • оптимізація завантаження фронтенда (залежить від попереднього параметра)
  • вулканізація (також залежить від першого параметра)
  • переміщення JavaScript і HTML за тіло сторінки
  • вимкнення підтримки веб-компонентів
Переміщення JavaScript і HTML за тіло сторінки
Це найпростіше, JS/HTML файли будуть підключені перед
</body>
замість приміщення в
<head>
, загалом рекомендується до використання.
CSS спеціально підключається в
<head>
завжди, оскільки базова стилізація зазвичай невелика і затримка в рендерінгу сторінці виходить не существеенная (особливо з використанням HTTP/2 Server і Push).
Вимкнення підтримки веб-компонентів
Теж досить проста настройка, яка, втім, працює тільки для кастомних тим (щоб не зламати адмінку з темою оформлення за замовчуванням). Призводить до повної фільтрації будь-яких HTML файлів, так само відключає завантаження полифиллов для веб-компонентів і JS файлів з
assets/Polymer
, що в цілому відключає все що пов'язано з веб-компонентами у фреймворку якщо воно вам не треба. Важливо розуміти, що багато готові модулі перестануть працювати, оскільки їх інтерфейс побудований повністю на веб-компонентах.
Кешування і стиснення
По-перше, фреймворк бере всі зібрані файли для підключення на сторінках з урахуванням залежностей і пакує файли в кеш. Для загальних файлів створюється трійка кешованих файлів з форматами css/js/html, аналогічні файли створюються для кожного ключа
assets
в кожному встановленому модулі + окремо створюються кеші мовних перекладів інтерфейсу + окремо кешується полифилл для веб-компонентів і зберігається нова структура залежностей із зазначенням вже на дані зібрані кешовані файли.
По-друге файли не просто потрапляють в кеш напряму — цьому передує деяка обробка. Фреймворк не намагається хапати зірок з неба в цьому питанні, зате працює дуже швидко.
Обробка CSS полягає в:
  • видалення зайвих пробілів і переказів рядків
  • оптимізації деяких конструкцій на зразок скорочення колірних HEX значень з 6 до 3 символів і конвертації RGB квітів у HEX
  • вбудовуванні невеликих зображень і подібних файлів (до 4 Кіб) в base64 вигляді, не вбудовані файли записуються в окремий список і будуть використані пізніше
  • коригуванню відносних шляхів з урахуванням нового цільового розміщення режим CSS
Обробка JS набагато більш убога, оскільки тягти повноцінний минификатор бажання немає, а інакше легко зламати код простим пошуком і заміною, так що обмежуємося наступним:
  • видалення гарантовано безпечних перекладів рядків
  • видалення однорядкових коментарів, які починаються з початку рядка
  • видалення багаторядкових коментарів, які починаються з початку рядка
  • звмена
    </script>
    на
    <\/script>
    , оскільки даний JS код може згодом бути використаний для вставки в HTML
  • ігноруються багаторядкові шаблони з ES2015 (починаючи від першого і закінчуючи останнім — все всередині виключається з обробки)
Дані минификации CSS/JS досить базові, але дозволяють за десяток мілісекунд перебрати весь проект, в той час як повноцінні минификаторы вимагають кілька секунд і з Gzip дають виграш лише до 5% (дані минификаторы ви можете запускати поверх побудованого кешу).
HTML обробляється ще менше — по суті весь JS код збирається разом і проганяється через згаданий минификатор, CSS для кожного веб-компонента так само проганяється через згаданий минификатор, під уникнення проблем вирізається
<link rel="import" href="../pol/pol.html">
з HTML.
Після побудови кешу генерується подія
System/Page/rebuild_cache
— кастомні минификаторы та інші речі на зразок побудови кастомного кеша можна виробляти в обробника цієї події.
У структурі кешу кожен файл супроводжується хешем в частині параметрів (приклади нижче в частині про HTTP/2 Server Push). Хеш вважається від вмісту файлу, так що при зміні одного файлу і перестроювання кешу зміниться хеш відповідного режим файлу, але не все кешу відразу.
Почитати докладніше про те, яку структуру мають допоміжні JSON файли з описом структури кешу, залежностями і іншим, можна в документації.
Вулканізація
Вулканізація це процес обробки HTML файлу, при якому CSS/JS код вбудовується в підсумковий HTML. Для CSS це не має жодного значення, оскільки Polymer поки не підтримує CSP-сумісні конструкції у всіх ситуаціях, так що CSS завжди вбудовується в підсумковий HTML.
Реалізація у фреймворку власна, в цілому про це можна почитати у відповідному проекті на GitHub.
Оптимізація завантаження фронтенда
Це цікавий механізм, покликання якого прискорити початкову обробку сторінки.
Як було описано вище є загальний CSS/JS/HTML код і специфічний для окремих сторінок. Так ось цей загальний код можна уявити собі як App shell. Оптимізація завантаження фронтенда полягає в тому, що спочатку на сторінці підключається тільки загальний JS/HTML, і тільки після того як він був завантажений і відпрацьований починається асинхронне завантаження і послідовне виконання решті частини JS/HTML коду (модулі повинні бути готові до того, що HTML код, який буде спільним для всіх сторінок, що може здійснитися раніше, ніж JS код залежностей даного модуля).
Даний підхід дозволяє значно прискорити першу рендеринг сторінок і таким чином дати користувачеві якщо не інтерактивну сторінку, щось корисне і максимально швидко.
Для оптимізованої завантаження будується окремий файл з додатковою структурою оптимізованого кеша.
HTTP/2 Server Push
При включеному кешуванні потрібні сторінці файли будуть супроводжуватися відповідними 'Link:` заголовками, що проксі-сервера (зразок CloudFlare, nghttpx) зазвичай перетворюють в Server Push, приклад заголовка:
Link:</storage/public_cache/CleverStyle:TinyMCE.html?602ca>; rel=preload; as=document
Link:</storage/public_cache/CleverStyle:System.css?fecf7>; rel=preload; as=style
Link:</storage/public_cache/CleverStyle:Uploader.js?2167a>; rel=preload; as=script
Link:</storage/public_cache/CleverStyle:System.html?14dd7>; rel=preload; as=document
Link:</storage/public_cache/CleverStyle:System.js?a0e9d>; rel=preload; as=script
Link:</storage/public_cache/CleverStyle:Static_pages.html?a292d>; rel=preload; as=document

Ще цікавіше стає якщо до цього додати оптимізацію завантаження фронтенда — тоді Server Push буде застосовуватися тільки для тих файлів, які підключаються спочатку, приклад тієї ж сторінки вище:
Link:</storage/public_cache/CleverStyle:System.html?14dd7>; rel=preload; as=document
Link:</storage/public_cache/CleverStyle:System.js?a0e9d>; rel=preload; as=script
Link:</storage/public_cache/CleverStyle:System.css?fecf7>; rel=preload; as=style

Server Push так само потраплять файли, які не були вбудовані в CSS з-за їх розміру, але які потрібні для відтворення сторінки (детальніше в документації).
Ну і, звичайно ж, Server Push спрацьовує лише один раз, після чого виставляється cookie щоб уникнути деградації продуктивності.
Чому вам може бути не потрібна кастомний система складання фронтенда
Якщо у вас процес розробки схожий на мій: LiveScript->JavaScript, SCSS->CSS, Jade->HTML — все з допомогою File Watchers автоматично генерується при кожній зміні і цілком потрапляє в Git, то ви можете обійтися без кастомних системи складання. Будь-яке редагування будь-якого файлу призводить до генерації відповідного артефакту і поки ви переключаєтеся в браузер щоб натиснути F5 (без включеного кешування у фреймворку) у вас все готово до роботи.
якщо у вас є кастомний система збирання, ви хочете додати кастомний систему збирання або замінити те, що робить фреймворк повністю?
Ваші найкращі друзі це два системых події:
  • System/Page/assets_dependencies_and_map
  • System/Page/rebuild_cache
З їх допомогою ви можете інтегруватися в роботу фреймворку і користуватися всіма зазначеними перевагами маючи/використовуючи додаткові артефакти/інструменти так, як вам зручно.
Також в документації описана структура кеша, так що ви можете підготувати артефакти вашої системи збирання в такому форматі, в якому це буде зрозуміло фреймворку і таким чином не використовувати систему складання фреймворку, але згодовувати йому власні артефакти в съедобном для нього форматі.
Наостанок
Завжди радий новим ідеям і конструктивним коментарям.
» Репозиторій на GitHub
Джерело: Хабрахабр

0 коментарів

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