Швидкий рендеринг з DOM шаблонізатором

Борис В.с.каплуновський (
BSKaplou
Борис В.с.каплуновський

Я досить довго працював над доповіддю і намагався зробити його настільки суперечливим, наскільки це можливо. І відразу почну з протиріччя – я в корені не згоден з тим, що веб-компонентами можна користуватися. Вже піднімалося питання про 300 Кбайтах, я глибоко впевнений, що 300 Кбайт для сторінки Javascripta – неприпустимо багато.

Сьогодні я розповім про досить глибокому подорожі у фронтенд. Почалося це подорож тоді, коли я виявив, що фронтенд aviasales.ru гальмує, і треба щось робити. Це подорож почалося півтора-два роки тому, і речі, про які я буду розповідати, – це стисле оповідання того, що я дізнався.

Самим критичним, на мій погляд, в продуктивності фронтенд-додатків є рендеринг. Всі ми знаємо, що робота з DOM – це така річ, яку потрібно намагатися уникати. Чим більше ви робите викликів до DOM API, тим повільніше працює ваш додаток.



Про що конкретно ми поговоримо? Про правила гри. На які речі в рендеренге, в роботі веб-додатки потрібно звертати увагу, які параметри є ключовими для бібліотеки шаблонізації для візуалізації, які є типи шаблонизаторов.

Далі я трохи пройдуся по кістках гігантів, це AngularJS і ReactJS, спробую розповісти, чим вони мені не подобаються, і чому вони гальмують. Розповім про те, що хорошого я знайшов в інших шаблонизаторах і про твір, яке ми створили, грунтуючись на всіх вищевикладених знаннях.


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



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

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

Наступна річ – це споживання ресурсів. На веб-сторінках важливі два основних показника: споживання процесора (якщо ви робите багато зайвих дій, ви греете процесор, і у нього не вистачає часу, щоб обрахувати анімацію на інтерфейс, або що-то просто домалювати), крім того, якщо ви створюєте багато зайвих об'єктів, це створює навантаження на garbage collector. Якщо ви створюєте навантаження на garbage collector, то періодично він буде викликатися, і чуйність вашого додатка буде падати.

І останній, але від цього не менш важливий пункт. Розмір бібліотеки. Якщо у вас single page application, 200-300, іноді навіть 400 Кбайт javascript ви можете собі дозволити. Проте ж, компонентний веб, в напрямку якого ми з вами весело рухаємося, передбачає, що сторінки будуються з різних веб-компонент. Більш того, ці веб-компоненти найчастіше вироблені в різних компаніях і приходять зі своїм пакетом.

Уявімо собі сторінку, на яку вставлено десяток віджетів: віджет курсів валют, погоду, авіаквитків, чорта в ступі чого… І кожен з цих компонент важить 300 Кбайт, і це тільки JS. Таким макаром, у нас запросто вийде сторінка, яка важить 5-10 Мбайт. Все б нічого, та Інтернет стає все швидше і швидше, але з'явилися мобільні пристрої, з'явилися повільні мережі, і якщо ви користуєтеся Інтернетом не в місті Москва, а де-небудь в Єкатеринбурзі, то 15 Мбайтні сайт виявиться для вас абсолютно неприпустимою розкішшю. Саме тому розмір бібліотеки, на мій погляд, критичний.

Трохи нижче я порівнюю кілька бібліотек, і не порівнюю полімери, не порівнюю з тієї причини, що 200 Кбайт для бібліотеки веб-компонентів – це занадто багато.



Отже, перейдемо до теми розмови – до шаблонизаторам.

Всі ми, хто займається розробкою веб, звикли вже до строкових шаблонизаторам. Рядкові шаблонизаторы – це шаблонизаторы, які в результаті своєї роботи повертають нам рядок. Рядок, яку пізніше ми вставляємо допомогою innerHTML в html. Це чудовий, древній, всім звичний механізм. Проте він має ряд недоліків. Головний недолік – те, що кожен раз, коли ви зробили шаблонизацию і робите вставку в innerHTML, доводиться викинути весь DOM, який там був і раніше вставити новий DOM.

Наскільки я пам'ятаю, робота з DOM дуже і дуже повільна. Якщо ви викинули 20 тегів з 30-ма атрибутами і вставили такі ж, ті ж 20 тегів з 10-ма атрибутами, то це займе значний час. 20 мілісекунд запросто. Крім того, рядкові шаблонизаторы не дозволяють залишати якоря для швидких апдейтів одиничних атрибутів, одиничних текстових нод і т. п.

Виявивши ці неоптимальності, ми почали шукати, як можна позбутися від цих недоліків, що можна з цим зробити? І перше, що підказав Goggle, це «Використовуйте DOM API». Це штука не дуже проста. Але у неї є плюси.



Скріншот з сайту jsperf. Бенчмарк, який бачить продуктивність строкових шаблонизаторов, вставляющих шматки html з innerHTML і DOM JS. Тут ми бачимо у верхній частині продуктивність на На е, і бачимо, що JSDOM API дозволяє прискорити рендеринг в кілька разів. Тут приблизно в три рази. У той же час на десктопних браузерах такого пекельного приросту продуктивності немає.

Google приблизно півроку тому почав обіцяти всім веб-розробникам «мобилогеддон». Це означає, що всі сайти, які не адаптовані під мобільні пристрої, респонсивные, адаптивні, будуть пессимистироваться в пошукові видачі. Це означає, що якщо ви не готові до мобільних пристроїв, просто трафік з Google на ваших сайтах значно зменшиться.

По суті, цей слайд наочно свідчить, що використовуючи DOM API, ви можете значно прискорити рендеринг на мобільних пристроях. Причому це відноситься не тільки до На ам. Як ви знаєте, всі сучасні На и і IOS-пристрої використовують один і той же движок WebKit, приблизно з одним набором оптимізації, а значить такий же приріст продуктивності ви отримаєте на всіх IOS пристроях, якщо будете рендери сторінки через DOM API.



Однак, DOM API – штука досить громіздка. Тут я навів п'ять основних викликів, з допомогою яких можна створювати ділянки DOM. Привів я їх приблизно в тому вигляді, в якому вони будуть зустрічатися в коді вашої програми, якщо ви будете створювати ділянки DOM безпосередньо через API.

Створення одного елемента, який раніше у вас вкладався в 15-17, може бути 30-50 символів, через DOM API запросто у вас виллється в 5-10 рядків коду. Час роботи програмістів цінно, а це значить, що ми не можемо замінити html на ручне програмування DOM.



Тут-то нам і необхідні шаблонизаторы. Як ви пам'ятаєте, рядкові шаблонизаторы повільні, і хочеться мати DOM шаблонизаторы, шаблонизаторы працюють через DOM API, але дозволяють користуватися всіма тими пляшками, до яких ми звикли, працюючи з звичайними шаблонізатором.

Отже, що ж нам дають DOM шаблонизаторы, крім можливості не використовувати рідну JSDOM API? Вони дозволяють зберігати DOM об'єкти в змінних для швидкого оновлення пізніше. Використовуючи DOM шаблонизаторы, можна використовувати один і той же ділянку DOM кілька разів.

Що я маю на увазі? Припустимо, ми відвідуємо сторінку веб-магазину. Користувачі заходять в одну категорію товарів, і заготовлені шаблони підставляються дані про одному списку товарів. Коли людина переходить в іншу категорію товарів, у ці ж шаблони підставляються інші дані. По суті, ми не пересоздаем DOM, ми використовуємо одні й ті ж ділянки DOM для відображення даних. Це дозволяє дуже сильно заощадити і на ресурсах процесору, і пам'яті, і іноді на часі програмістів.

Після усвідомлення цієї думки, що інструмент, який мені потрібен, – це DOM шаблонизаторы, ми пішли дивитися, що ж вже існує в індустрії, чим можна скористатися, щоб швидко і якісно працювати з DOM, і швидко його рендери?

Далі я розповім, де, на мій погляд, оступилися гіганти.



Перший гігант, про який я хочу розповісти, це AngularJS.

AngularJS, мені здається, оступився прямо на самому старті. Якщо ви ним користувалися, то, напевно, помітили, що всі шаблони передаються на клієнт у вигляді або DOM ділянок (що є не дуже хорошим стилем), або у вигляді рядків. Після того, як бібліотека завантажилася, Angular змушений скомпілювати ваші рядки або DOM в реальні шаблони. Це відбувається на клієнта.

Уявімо собі цікаву ситуацію. Користувач заходить на сторінку, вантажить весь JS, якого для Angular додатків може бути досить багато – 100-200-300 Кбайт запросто. Після цього кожен шаблон з розбором рядків починає компилиться. Це призводить лише до однієї речі – первісна завантаження Angular додатків може із-за цієї компіляції (під час якої користувачі займаються чим завгодно, окрім роботи з сайтом) тривати півсекунди, секунду. Я зустрічав сайти, на яких процес компіляції шаблону займав навіть дві секунди. Більш того, ця проблема наростає як сніжний ком: чим більше шаблонів у вашому додатку, чим складніше ваше single page application, тим більше часу ми витрачаємо на первісну компіляцію шаблонів.



Наступна проблема в Angular. Ми всі пам'ятаємо, що Angular продавався нам панами з Google, як перший найкрутіший фреймворк з двостороннім биндингом. Причому, цей двосторонній биндинг реалізується через т. зв. $watcher'и, які ви вішаєте на структури даних для подальшого відображення їх в DOM. На слайді цікава картинка, але ви на нього не дивіться. Цікавим у ній є тільки цей чудовий цикл, в ході якого обходяться всі $watch'і, на всіх даних, які у вас існують в системі. Причому, звичайно ж, документації і у всіх туториалах, вам ніхто не буде розповідати, що за $watcher'ами потрібно стежити. Це призводить буквально до наступного. У якийсь момент ваше чудове додаток починає раз в 100 мс добре пригальмовувати. Починають гальмувати анімації, починає текти пам'ять. Виявляється, що допускати багато $watcher'ів просто не можна. Як тільки ви допустили багато $watcher'ів, ваш додаток починає спонтанно гальмувати. Тут ви починаєте хитро викручуватись, йти на що завгодно, скорочувати кількість $watcher'ів, відмовлятися у додатку від двостороннього биндинга, заради якого ви брали Angular, тільки щоб позбутися від гальм.



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

Якщо ви користувалися ng-repeat, то ви, напевно, бачили, що якщо ви запхнете 100 елементів у неї, і ще там будуть $watcher'и, то рендеритись все це буде дуже довго. Проблема настільки широка, що працюючи з Angular (наша попередня версія видачі була побудована саме на Angular), нам прийшло написати свій ng-repeat. Це зробив наш співробітник Антон Плєшивцев і розповідав про це на безлічі конференцій. Крім цього, 50 Кбайт мінімізованого розміру бібліотеки, на мій погляд, все-таки забагато. Тобто за що ви платите? Якщо ви дивитеся код Angular, то ці 50 Кбайт зашита власна система класів, туди зашита дуже неякісна, на мій погляд, версія Underscore. І це все ви отримуєте абсолютно безкоштовно в рамках 50-ти Кбайт коду.



Наступне. Набагато більш якісний фреймворк, на мій погляд, це ReactJS. Судячи з того, як вирує Інтернет, кожен перший програміст, навіть не завжди фронтендщик, користувався Angular і в захваті від нього. Я не вважаю, що virtualDOM може прискорити роботу з DOM.

Дивіться, що нам пропонує virtualDOM. VirtualDOM є джерелом, з якого ReactJS створює справжній DOM, тобто крім реального DOM, від створення якого ви нікуди не дінетесь (virtualDOM всього лише дозволяє його створювати), ReactJS в пам'яті тримає ще virtualDOM, це називається надлишковістю.

VirtualDOM трохи менше справжнього DOM, може бути раз в 5. Однак, насправді, ви змушені тримати в пам'яті дві копії virtualDOM. Тобто ви тримаєте справжній DOM, ви тримаєте відображення справжнього DOM в virtualDOM, крім того, кожен раз, коли ви збираєтеся робити div у virtualDOM, ви робите ще одну копію DOM. У вас був один DOM, тепер у вас їх три – молодці! Більше того, на кожну зміну даних, ви створюєте ще одну копію virtualDOM, це і є третя копія, однак, ви створюєте її з нуля.

Це створює серйозне навантаження на garbage collector і на процесор. Крім того, на мій погляд, бібліотека все ще жирна – 35 Кбайт. І знову ж, хлопці вирішили намалювати свою систему класів, намалювати свій lowdash, оригінальний чомусь не влаштовував, і все це запхали в 35 Кбайт. Крім того, там упакований virtualDOM з міфічним алгоритмом, який нібито дає величезну продуктивність.

Наступна проблема virtualDOM і React зокрема, це те, що ReactJS нічого не знає про семантику ваших даних. Давайте, подивимося цей дуже простий приклад.



Тут ми бачимо два вкладених <div>, і у них вкладено ще один тег <i>. Щоб змінити value через virtualDOM, алгоритм virtualDOM всередині React змушений порівняти три тега і одне текстове значення. Якщо ми знаємо семантику даних, для нас досить порівняти тільки текстове значення, тому що в шаблоні написано, що всередині одного <div> завжди іншої <div>, усередині наступного <div> тег <i>… Навіщо нам їх порівнювати кожен раз? Це реально оверхед.

Крім того, якщо ви програмували на React, то ви знайомі з такою штукою як pure-render-mixin. Суть його в тому, щоб позбутися від роботи c virtualDom'ом. Трапляється дуже цікава ситуація близька до комічною. Спочатку господа з Google пару років продавали нам React як штуку, яка з допомогою virtualDOM пекельно прискорює роботу з DOM, а потім виявляється, що щоб швидко працювати з DOM, потрібно виключити virtualDOM. Молодці, добре зробили.

А тепер дещо інше. Захотілося пошукати – бути може, на планеті є бібліотеки, є люди, які зробили щось краще. Я не намагався знайти одну бібліотеку, срібну кулю, але хотів підглянути в бібліотеках речі, які можна було б використовувати або для прискорення React, або для створення власної бібліотеки. І ось, що я знайшов.



Я розгляну дві цікаві бібліотеки. Перша з них – RiotJS.

На мій погляд, RiotJS – це правильна AngularJS, просто тому що розмір бібліотеки 5 Кбайт. Хлопці взяли абсолютно ті ж ідеї, що були в AngularJS, але не стали переписувати lowdash, просто сказали: «Навіщо? Він вже написаний». Хлопці не стали переписувати, винаходити свою систему класів, нічого не стали робити. Отримали бібліотеку в 5 Кбайт. Продуктивність більше, ніж у AngularJS, ідеї абсолютно ті ж. І більше того, шаблони, що використовуються в RiotJS, використовують семантику даних, що дає непоганий приріст продуктивності. Але залишилася і проблема – компіляція шаблонів все ще відбувається на клієнті. Це не дуже швидко, але вже набагато краще.



Наступна бібліотека, яка привернула мою увагу, це PaperclipJS.

PaperclipJS використовує ряд дуже цікавих оптимізацій. Зокрема, для створення шаблонів використовується cloneNode, а далі я покажу, що він дає великий приріст продуктивності, але це рішення дозволяє PaperclipJS бути більш прозорою, більш зрозумілою для девелопера.

Але і в цієї бібліотеки знайшлося два недоліки: вона досить велика – 40 Кбайт, це більше, ніж React; і, незважаючи на хороші ідеї, розробка ведеться досить мляво. Цій бібліотеці вже пару років, проте вона досі не вийшла з стадії beta.

Поспілкувавшись з цими бібліотеками та іншими бібліотеками, начитавшись гуру html5, я зміг придумати наступний список технік, які дозволяють прискорити роботу з DOM.



Перша річ – це VirtualDOM. Його плюси я довго шукав, і знайшов тільки один – він дозволяє скоротити кількість викликів до DOM, тим самим піднявши продуктивність. Однак, оверхед на створення копії DOM, на мій погляд, все-таки значний. Складний алгоритм порівняння, над яким досі стоїть завіса таємниці, що використовується в React, він не такий швидкий, як нам про нього обіцяють. Щоб зрозуміти, як вона працює, ви витратите дня два. І всій цій магії, про яку розповідали в блогах, там немає, на мій погляд. Крім того, virtualDOM сидить на тій проблемі, що алгоритм нічого не знає про структуру даних. Поки ми нічого не знаємо про структуру даних, всі наші врайперы, всі наші елементи верстки, негативно позначаються на продуктивності, тому що алгоритм virtualDOM повинен брати участь у їх порівнянні.



Техніки, які були відомі дуже давно – це використання cloneNode, про який я вже говорив в рамках PaperclipJS і DocumentFragment. Ці дві техніки використовуються для підвищення продуктивності. Ні одна, ні інша техніка, наскільки мені відомо, не використовується ні в AngularJS, ні в ReactJS. Однак скріншот бенчмарка з jsperf наочно показує, що це дозволяє прискорити роботу з DOM, як мінімум в три рази. Досить непогана практика, дуже раджу користуватися.



Наступна техніка, яка лежить абсолютно на поверхні, більше того, неявно зустрічається навіть у туториале за React, – це створення DOM ділянок заздалегідь. Що я маю на увазі? Припустимо, людина заходить на сторінку інтернет-магазину електронних чайників. Вводить назву чайника, назва фірми чайника, який хоче придбати. У цей момент на сервер відправляється пошуковий запит. Якщо ваші серверні програмісти швидкі і блискавично, то ви можете отримати відповідь за 20 мс, ці 20 мс користувач практично нічого не робить. І в цей момент ми можемо створити структуру DOM під дані, які повернуться з нашого сервера. Досить проста практика. Не знаю, чому вона не отримала широкого застосування. Я її користуються ве, дуже круто виходить.

Отже, що виходить? Ми шолом запит на сервер, поки ми чекаємо відповіді з сервера, ми готуємо структури DOM під дані, які мають до нас прийти з сервера. Коли до нас приходить відповідь з сервера, насправді нам його ще потрібно розібрати. Найчастіше, це не просто прийняти Json, але і як-то адоптувати його. Якщо до цього моменту DOM у нас уже готовий, то ті 2-3-4 мс, які у нас є для роботи JS ми можемо витратити на адаптацію і на вставку даних в DOM і додавання даних на сторінку.

Дуже раджу використовувати це, причому явно в фрейморках ця штука не підтримується, але ви можете руками створити елемент при відправці запиту на сервер.

Отже, спорядившись всіма цими знаннями, і знайшовши трошки вільного часу вночі та у вихідні, я вирішив написати невеликий прототип, з яким ми стали далі працювати.



Це шаблонизатор temple. Він дуже простий, дуже маленький, там буквально менше 2000 рядків коду.

Якими ж властивостями він володіє? Шаблони компілюються на момент складання JavaScript код, тобто на клієнті ніякої роботи, крім завантаження JavaScript коду не робиться. Можливість створювати шматки DOM заздалегідь підтримана в бібліотеці. Бібліотека дозволяє легко і просто переиспользовать шаблони. Розмір бібліотеки, на мій погляд, більш ніж скромний в мінімізованому і gzip'стані – це всього 700 байт. Більш того, річ, яка мені в ній подобається найбільше – це максимально швидке оновлення DOM.

Далі спробуємо по шматочках розібрати, як же це все зроблено і працює.



Структура шаблону вкрай проста і примітивна. Це вуса, всередині яких додаються змінні.



Все досить очевидно, ніякої магії. Крім цього, підтримує дві конструкції. Це forall ітерація по ключу для циклів і розгалуження if. Не підтримуються вираження. Деякий час назад, до появи React, в індустрії домінувала думка, що не можна ні в якому разі заважати View і модель. Я досі вірю, що це правильний підхід, тому якщо хочеться використовувати складні вирази, краще винести це окремий компонент. Якщо ви пам'ятаєте, є такі паттерные Presenter або ViewModel, якщо вам потрібно складно готувати дані відображення в шаблони, краще це зробити там, і не затягувати вираження в шаблони.

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

Як виглядає робота з темплом.



Ми беремо іменований шаблон з темпла, апдейтим дані в ньому викликом update. І вставляємо його в DOM. Фактично це дуже схоже на роботу з звичайним DOM API. Після того, як ми скористалися шаблоном, ми можемо прибрати його з DOM викликом remove. Все дуже просто.



Як робиться завчасне створення DOM? Припустимо, ми послали на сервер запит і знаємо, що з сервера нам повинен прийти набір чайників. В цей момент ми говоримо пулу шаблонів: «Створи кеш на 10 чайників». Він створюється, і коли ми наступного разу будемо робити такий самий виклик get, реальної роботи з DOM не буде, ми отримаємо вже підготовлений і срендеренный шаблон. Тобто отримаємо шаблон для вставки в DOM миттєво.

Коли цим найзручніше користуватися? Дивіться, ми надсилаємо запит на сервер і у нас є 20 мс, насправді, звичайно, не 20, швидше за все це 200-300 мс, за цей час ми можемо накэшировать мільйони DOM нод, тобто часу достатньо.

Другий варіант – це кешувати шаблони, коли ми очікуємо DOMContentLoaded.

З DOMContentLoaded є така проблема, що дуже багато обробників підписуються на це подія і в результаті в момент приходу цього події прокидається чортова хмара скриптів, які все по callback'у починають обробляти, і після цієї події близько 100 мс додаток спить. Воно глибоко вважає там щось. Щоб скоротити це очікування, можна кешування DOM шаблонів зробити заздалегідь, до того, як ця подія до нас прийде.



Швидке внесення змін в DOM. Тут я наводжу простий і зрозумілий виклик, він дуже схожий на те, як робиться update React. Всі пам'ятають виклик у React setState, різниця тільки в глибині стека. Якщо ви бачили, наскільки глибоко, скільки викликів функцій робить React, перед тим як зробити цільове дію (а цільове дію у нас, швидше, таке – просто підставити в DOM це значення), то знаєте, що для React це може бути глибина стека 50-60, може бути більше.

Кожен виклик, особливо в динамічних мовах, як JavaScript, дається нам зовсім не безкоштовно. Це не так повільно, як DOM виклик, але все ще не безкоштовно. Темпл дозволяє робити цю заміну з глибиною стека =2, тобто, по суті, викликається update, з нього викликається функція, яка замінює значення. По суті, це функція value. І з глибиною стека =2, ми отримуємо цільове дію. При цьому виклик update рекомендується використовувати тоді, коли ми хочемо змінити відразу кілька значень. Якщо ми хочемо змінити одне, то це можна ще швидше – прямим викликів property і тоді ця підстановка буде зроблена з глибиною стека =1, швидше фізично неможливо.



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



Далі спробую вам продати цей інструмент з допомогою бенчмарків. Перший бенчмарк. Внизу я завжди наводжу посилання на бенчмарк, і пам'ятаємо, що на jsperf «більше» не означає «краще». В даному випадку червоне – це Темпл, і синє – це React. C React я сравниваюсь, тому що React, напевно, саме швидке рішення, він раз у 5 швидше, ніж Angular зазвичай. Отже, що ж ми тут бачимо? Початкова ініціалізація у Temple робиться на 30 відсотків швидше в Chrom'е, і на відсотків 10-15 в Firefox е. За рахунок чого це виходить? Всередині, в кінцевому підсумку, використовує.ться ті ж create element, create text, node appendchild. Однак у Темпле майже ніколи немає глибини стека більше двох. По суті, ми економимо час виключно на дзвінки всередині бібліотеки. Ті 35 Кбайт JavaScript, які ви завантажуєте для використання React, відливаються вам цією різницею в продуктивності – довгі стеки.



Наступний бенчмарк, який ми винайшли і прогнали – це soft Update. Soft Update – це коли ми підставляємо в шаблон ті ж дані, які туди вже були підставлені. Це те місце, де повинен прокинутися virtualDOM і сказати: «Хлопці, дані вже є, нічого робити не треба». Скажу відразу, що для чистоти експериментів на virtualDOM я не використовував pure-render-mixin. І виявилося, що оптимізації в самому браузері дозволяють робити це в чотири рази швидше. VirtualDOM вносить гальма в чотири рази.



Підемо далі, hard update. Hard Update – це сценарій, при якому в шаблоні оновлюється не один ділянка даних, а всі дані, які є. Знову ж таки не використовуємо pure-render-mixin, але він тут би був і даремний. І отримуємо ще більш цікаві дані. При hard update виграш Темпла в десятки разів, просто тому що там немає virtualDOM.

VirtualDOM на практиці виявився дуже ресурсномісткою операцією. І якщо ви глибоко програмували React-додатки, то досить швидко зіткнулися з тим, що роботу з virtualDOM потрібно скоротити. Я в цій ідеї досяг повного перфекціонізму і вважаю, що virtualDOM, як ідея, поганий, і його треба викинути. Це зробить React легше Кбайт на 20, і швидше у 10 разів.



Темпл, який ми робили, дуже маленький. Aviasales – не Facebook, у нас немає мільйонів инженеро-годин, у нас є тільки… Як ви розумієте, розробка таких бібліотек – не дуже продуктова фіча, і в робочий час займатися цим не виходить. Цим можна займатися вночі невеликою групою ентузіастів. Тому бібліотека дуже маленька. Темпл не пропонує роботу з подіями. У React є DOM delegate, в AngularJS є своя робота з подіями. Але я і не вважаю, що потрібно роботу з подіями інтегрувати в шаблонизатор. Для роботи з подіями можна використовувати стандартні бібліотеки. Ми використовуємо domdelegate від Ftlabs. Ftlabs – це IT-підрозділ «Financial Times». Хлопці зробили дуже гарну, дуже просту і продуктивну бібліотеку, її розмір, якщо не помиляюся, менше 5 Кбайт. Ми її використовуємо в парі з Темпл, і задоволені результатами.



На користь своїх попередніх бенчмарків і слів, хочу сказати, що на даний момент Темпл ми використовуємо вже в двох проектах: це мобільна версія aviasales.ru і нова пошукова видача aviasales.ru. У мобільній версії всі шаблони перетворюються в 10 Кбайт коду, мова йде про мінімізованому і стислому коді, і все додаток займає 58 Кбайт. Мені здається, це непоганий розмір для досить складного single page програми.

Наступним додатком, яке ми розробляли, була нова пошукова видача, там шаблони вже займають 15 Кбайт, і все додаток займає 70 Кбайт. Також було кілька інтеграцій з віджетами, але вони менш цікаві. Однак, 70 Кбайт для single page application, мені здається це гарний показник. Особливо якщо порівнювати з бібліотеками, які важать 200.



Власне, це відкрито в open source зараз. Можете подивитися його, погратися з ним url на слайді. Воно все ще досить сиро.

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

Контакти

BSKaplou
bk@aviasales.ru

Ця доповідь — розшифровка одного з кращих виступів на конференції фронтенд-розробників FrontendConf. Ми вже відкрили підготовку до 2017 року, а підписавшись на список розсилки конференції Ви отримаєте 8 кращих доповідей минулого року.

Найскладніша секція майбутньої конференції HighLoad++ це "Продуктивність фронтенда". Фронтенд став великим, це вже повноцінний софт зі своєю архітектурою, моделями і даними (а не просто інтерфейс, як було раніше). Саме в цьому розрізі ми і вивчаємо його на цій секції.

Ось деякі з запланованих доповідей:

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

0 коментарів

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